[TK] 寻宝游戏
在树上标记若干个点,求出从某个点走过全部点并回到该点的最小路径. 有多次询问,每次询问只改变一个点.
首先是一个暴力的思路.
会发现,从标记点中的其中一个开始走,结果一定更优,并且无论从哪个点开始走,其结果都是相同的. 假若不考虑还要走回来,那么答案就应该是标记点两两之间路径和的最小值,至于还要走回来,可以发现走回来的最优策略相同,因此答案乘二即可.
因此这个题就转化为求标记点两两之间路径和的最小值. 考虑到:将这些点按 DFS 序排序,得到的结果会是最小的. 为了使答案最小,可以想到将相邻的点都放到一起,或者说在这里是将距离最近的点放到一起,因此我么用节点在 DFS 序上的距离来刻画实际距离,因此按 DFS 序排序.
随后,可以想到对每一对相邻的点跑一遍 LCA,求出简单路径长. 可以发现,假如我们将 \(dist(1,n)\) 也加入答案的话,正好在每个点都出入了一遍,因此求出来的正好是走一个来回的贡献.
贴上暴力代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace reader{
template<typename T>
inline void read(T& x){
x=0;bool sym=0;char c=getchar();
while(!isdigit(c)){sym^=(c=='-');c=getchar();}
while(isdigit(c)){x=x*10+c-48;c=getchar();}
if(sym)x=-x;
}
template<size_t N>
inline void read(char (&str)[N]){
size_t n=0;char c=getchar();
while(n<N-1&&!isspace(c)){str[n]=c;c=getchar();++n;}
str[n]=0;
}
template<typename T,size_t N>
inline void read(T (&a)[N],int range=N){
for(int i=1;i<=range;++i){read(a[i]);}
}
template<typename T,typename... Args>
inline void read(T& x,Args&... args){
read(x);read(args...);
}
template<typename T,typename T2>
inline void readarray(T& x,T2& args){
read(x);read(args,x);
}
template<typename func,typename... Args>
inline void readact(int x,function<func>fu,Args&... args){
for(int i=1;i<=x;++i){
read(args...);
fu(args...);
}
}
}
using namespace reader;
int n,m;
int fa[100001][17];
int w[100001][17];
struct edge{
int to,w;
};
int deep[100001],dfn[100001],dfncnt;
vector<edge>e[100001];
void dfs(int now,int last,int lastw,int nowdeep){
dfn[now]=++dfncnt;
deep[now]=nowdeep;
fa[now][0]=last;
w[now][0]=lastw;
for(int i=1;i<=16;++i){
fa[now][i]=fa[fa[now][i-1]][i-1];
w[now][i]=w[fa[now][i-1]][i-1]+w[now][i-1];
}
for(edge i:e[now]){
if(i.to!=last){
dfs(i.to,now,i.w,nowdeep+1);
}
}
}
int lca(int x,int y){
// cout<<"lca "<<x<<" "<<y<<endl;
if(deep[x]<deep[y]) swap(x,y);
int res=0;
for(int i=16;i>=0;--i){
if(deep[x]-deep[y]>=(1<<i)){
// cout<<"jump "<<x<<" "<<fa[x][i]<<" "<<w[x][i]<<endl;
res+=w[x][i];
x=fa[x][i];
}
}
// cout<<"jd "<<res<<" "<<x<<" "<<y<<endl;
if(x==y) return res;
for(int i=16;i>=0;--i){
if(fa[x][i]!=fa[y][i]){
res+=w[x][i]+w[y][i];
x=fa[x][i];
y=fa[y][i];
}
}
if(x==y) return res;
return res+w[x][0]+w[y][0];
}
vector<int>g;
bool vis[100001];
signed main(){
// freopen("game1.in","r",stdin);
// freopen("out.out","w",stdout);
read(n,m);
function<void(int,int,int)>f=[](int x,int y,int z){e[x].push_back({y,z});e[y].push_back({x,z});};
int x,y,z;readact(n-1,f,x,y,z);
dfs(1,0,0,1);
while(m--){
read(x);
if(!vis[x]){
vis[x]=true;
g.push_back(x);
}
else{
vis[x]=false;
sort(g.begin(),g.end());
g.erase(lower_bound(g.begin(),g.end(),x));
}
sort(g.begin(),g.end(),[](int a,int b){return dfn[a]<dfn[b];});
int ans=0;
// cout<<"g:";
// for(int i:g)cout<<i<<" ";cout<<endl;
for(int i=0;i<=(int)g.size()-2;++i){
// cout<<g.size()-2<<endl;
// cout<<g.size()<<"act "<<i<<endl;
ans+=lca(g[i],g[i+1]);
}
ans+=lca(g.front(),g.back());
printf("%lld\n",ans);
}
}
需要注意的是这里这个 size()-2
一定要强转,这玩意是 unsigned
减不成负数,直接给我 for 循环搞炸了.
然后我们来考虑正解. 注意到题目中每次询问只改变一个点这条性质我们还没用到,考虑改变一个点会对答案有什么影响.
假设我们要加入一个点 \(x\),距离 \(x\) 的 DFS 序最近的左右两个标记点分别是 \(y,z\),可以发现,加入前这一段是 \(dist(y,z)\),加入后会变成 \(dist(y,x)+dist(x,z)\),因此贡献即为 \(dist(y,x)+dist(x,z)-dist(y,z)\),同理,删除点的话就变成减去. 这样我们就把更新答案的复杂度变成 \(\log n\) 了.
做这个题的时候,调 STL 调的很难受,STL 的迭代器确实动不动就 RE
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace reader{
template<typename T>
inline void read(T& x){
x=0;bool sym=0;char c=getchar();
while(!isdigit(c)){sym^=(c=='-');c=getchar();}
while(isdigit(c)){x=x*10+c-48;c=getchar();}
if(sym)x=-x;
}
template<size_t N>
inline void read(char (&str)[N]){
size_t n=0;char c=getchar();
while(n<N-1&&!isspace(c)){str[n]=c;c=getchar();++n;}
str[n]=0;
}
template<typename T,size_t N>
inline void read(T (&a)[N],int range=N){
for(int i=1;i<=range;++i){read(a[i]);}
}
template<typename T,typename... Args>
inline void read(T& x,Args&... args){
read(x);read(args...);
}
template<typename T,typename T2>
inline void readarray(T& x,T2& args){
read(x);read(args,x);
}
template<typename func,typename... Args>
inline void readact(int x,function<func>fu,Args&... args){
for(int i=1;i<=x;++i){
read(args...);
fu(args...);
}
}
}
using namespace reader;
int n,m;
int fa[100001][17];
int w[100001][17];
struct edge{
int to,w;
};
int deep[100001],dfn[100001],redfn[100001],dfncnt;
vector<edge>e[100001];
void dfs(int now,int last,int lastw,int nowdeep){
dfn[now]=++dfncnt;
redfn[dfncnt]=now;
deep[now]=nowdeep;
fa[now][0]=last;
w[now][0]=lastw;
for(int i=1;i<=16;++i){
fa[now][i]=fa[fa[now][i-1]][i-1];
w[now][i]=w[fa[now][i-1]][i-1]+w[now][i-1];
}
for(edge i:e[now]){
if(i.to!=last){
dfs(i.to,now,i.w,nowdeep+1);
}
}
}
int lca(int x,int y){
// cout<<"lca "<<x<<" "<<y<<endl;
if(deep[x]<deep[y]) swap(x,y);
int res=0;
for(int i=16;i>=0;--i){
if(deep[x]-deep[y]>=(1<<i)){
// cout<<"jump "<<x<<" "<<fa[x][i]<<" "<<w[x][i]<<endl;
res+=w[x][i];
x=fa[x][i];
}
}
// cout<<"jd "<<res<<" "<<x<<" "<<y<<endl;
if(x==y) return res;
for(int i=16;i>=0;--i){
if(fa[x][i]!=fa[y][i]){
res+=w[x][i]+w[y][i];
x=fa[x][i];
y=fa[y][i];
}
}
if(x==y) return res;
return res+w[x][0]+w[y][0];
}
//vector<int>g;
bool vis[100001];
std::set<int> s;
std::set<int>::iterator it;
signed main(){
// freopen("game1.in","r",stdin);
// freopen("out.out","w",stdout);
read(n,m);
function<void(int,int,int)>f=[](int x,int y,int z){e[x].push_back({y,z});e[y].push_back({x,z});};
int x,y,z;readact(n-1,f,x,y,z);
dfs(1,0,0,1);int ans=0;
while(m--){
read(x);x=dfn[x];
if(!vis[redfn[x]]){
s.insert(x);
// g.push_back(x);
}
int y=redfn[(it=s.lower_bound(x))==s.begin()?(*--s.end()):*(--it)];
int z=redfn[(it=s.upper_bound(x))==s.end()?*s.begin():*it];
// sort(g.begin(),g.end(),[](int a,int b){return dfn[a]<dfn[b];});
// auto ity=lower_bound(g.begin(),g.end(),dfn[x],[](int a,int b){return dfn[a]<dfn[b];});
// auto itz=upper_bound(g.begin(),g.end(),dfn[x],[](int a,int b){return dfn[a]<dfn[b];});
// int y=((ity==g.begin())?g.back():*--ity);
// int z=((itz==g.end())?*g.begin():*itz);
if(vis[redfn[x]]){
s.erase(x);
// g.erase(lower_bound(g.begin(),g.end(),dfn[x]));
}
x=redfn[x];
if(vis[x]){
vis[x]=false;
ans-=lca(x,y)+lca(x,z)-lca(y,z);
}
else{
vis[x]=true;
ans+=lca(x,y)+lca(x,z)-lca(y,z);
}
printf("%lld\n",ans);
}
}
[TK] 寻宝游戏的更多相关文章
- [BZOJ3991][SDOI2015]寻宝游戏
[BZOJ3991][SDOI2015]寻宝游戏 试题描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择 ...
- 【BZOJ】【3991】【SDOI2015】寻宝游戏
dfs序 我哭啊……这题在考试的时候(我不是山东的,CH大法吼)没想出来……只写了50分的暴力QAQ 而且苦逼的写的比正解还长……我骗点分容易吗QAQ 骗分做法: 1.$n,m\leq 1000$: ...
- 树形结构的维护:BZOJ 3991: [SDOI2015]寻宝游戏
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- bzoj 3991: [SDOI2015]寻宝游戏
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- 【BZOJ3991】【SDOI2015】寻宝游戏
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然 ...
- Loj #2494. 「AHOI / HNOI2018」寻宝游戏
Loj #2494. 「AHOI / HNOI2018」寻宝游戏 题目描述 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得 ...
- 【BZOJ5285】[HNOI2018]寻宝游戏(神仙题)
[BZOJ5285][HNOI2018]寻宝游戏(神仙题) 题面 BZOJ 洛谷 题解 既然是二进制按位的运算,显然按位考虑. 发现这样一个关系,如果是\(or\)的话,只要\(or\ 1\),那么无 ...
- BZOJ.5285.[AHOI/HNOI2018]寻宝游戏(思路 按位计算 基数排序..)
BZOJ LOJ 洛谷 话说vae去年的专辑就叫寻宝游戏诶 只有我去搜Mystery Hunt和infinite corridor了吗... 同样按位考虑,假设\(m=1\). 我们要在一堆\(01\ ...
- bzoj 3991: [SDOI2015]寻宝游戏 虚树 set
目录 题目链接 题解 代码 题目链接 bzoj 3991: [SDOI2015]寻宝游戏 题解 发现每次答案就是把虚树上的路径*2 接在同一关键点上的点的dfs序是相邻的 那么用set动态维护dfs序 ...
- 5285: [Hnoi2018]寻宝游戏
5285: [Hnoi2018]寻宝游戏 链接 分析: 从下面依次确定运算符号,然后在确定的过程中,需要确定的位数会逐渐减少.比如最后有一个1,如果在从下往上确定了一个or 1,那么再往前可以随便选了 ...
随机推荐
- 机器学习:详解多任务学习(Multi-task learning)
详解多任务学习 在迁移学习中,步骤是串行的,从任务\(A\)里学习只是然后迁移到任务\(B\).在多任务学习中,是同时开始学习的,试图让单个神经网络同时做几件事情,然后希望这里每个任务都能帮到其他所有 ...
- hbuilderx+香蕉云编生成ios证书和上架教程
现在很多公司都使用uniapp作为底层框架来开发app应用,而uniapp的开发工具hbuilderx云打包的时候,需要证书和证书profile文件. 假如是ios应用,则还需要上架到appstore ...
- 3、SpringBoot2之配置文件
3.1.环境搭建 3.1.1.在project创建新module 3.1.2.选择maven 3.1.3.设置module名称和路径 3.1.4.module初始状态 3.1.5.引入springbo ...
- 【Lodop】01 Lodop手册阅读上手
官方网站: http://www.c-lodop.com/index.html 版本:6.2.2.6 一.概述 Lodop是一款用于WEB打印开发的专业WEB打印控件 控件发布包有3个系统文件组成,主 ...
- 【Layui】16 表单元素 Form
文档地址: https://www.layui.com/demo/form.html 表单元素: 1.输入框 2.密码框 3.下拉列表 4.单选框 5.复选框 6.文档域 7.富文本 8.开关 单行输 ...
- ( Ubuntu环境下 )Vim插件推荐-Python自动补齐Vim插件jedi-vim的安装(使用插件管理器vundle进行安装)
Ubuntu系统下,为 Vim 安装python自动补齐的插件 jedi-vim . 1. jedi-vim安装依赖 首先,jedi-vim插件需要当前Vim版本支持python,在终端输 ...
- BMC Genomics | 综合代谢组学和转录组学分析揭示了菊花黄酮和咖啡酰奎宁酸的生物合成机制
杭白菊是一种流行的药用和食用植物,主要通过黄酮类和咖啡酰奎宁酸(CQAs)的存在发挥其生物活性.然而,菊花头状花序中黄酮和CQA生物合成的调控机制尚不清楚. 本研究采用高效液相色谱法测定了菊花头状花序 ...
- Django框架创建运行最小程序过程记录
基于 python语言 Django web框架下 用pycharm创建,修改,运行 最简单程序.旨在过程 ========================================== 步骤一 ...
- [考试记录] 2024.7.15 csp-s模拟赛4
2024.7.15 csp-s模拟赛4 T1 传送带 题面翻译 有一个长度为 \(n\) 的一维网格.网格的第 \(i\) 个单元格包含字符 \(s_i\) ,是"<"或&q ...
- 【全】CSS动画大全之按钮【b】
效果预览 代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...