最小行走距离(dfs+虚树)
好吧,感觉标题的名字取得有一点奇葩呢!源于我的高度概括
所以再做一些具体放阐述,这个问题大概意思是:在树上指定了一些点,你可以从树上的任意点出发,求走完所有指定点的最小行走距离(可存在一条边重复走,走几次这条边的权值就加几次)。
这个问题呢,我是在训练考试中碰到的,但是题目和要求做过一些小修改,为了更好的理解这种思想,我搜索到了这样一道最原始的题。
[SDOI2015]寻宝游戏
如何保证距离最短?我们可以做出这样的分析:
对于一个点,我们可以向子树走,也可以向父亲走。向父亲走肯定没有向子树走更优,因为你迟早要走一遍子树,先走父亲不过是又多加了一段重复走的路。(因为还要下来)
所以对于每一个点,我们选择先走子树再往上走->dfs序
而为什么说是虚树呢?其实我觉得不说虚树这个概念也没什么,因为我们只需要走那些被选择的点,而没有走完整棵树。
因为是动态的,我们考虑在当前状态下再加入一个点和删去一个点要怎么处理:
对于一条链,每一次变化时找到这个点插入的前驱pre和后继nxt,加入的时候删除dis(pre,nxt),加上dis(pre,x) + dis(x,pre),删除同理。
然后这个dfs序就用set维护
#include<bits/stdc++.h> #define N 100003 #define LL long long using namespace std; int read() {int x=0,f=1;char s=getchar();while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}return x*f; } struct EDGE{int to,nextt,val; }w[N*2]; int vis[N],tot=0,head[N],now[N],cnt=0; int f[N][23],dep[N],id[N],pos[N];LL valuee[N]; LL ans=0; set<int>s; void add(int a,int b,int c) {tot++;w[tot].to=b;w[tot].nextt=head[a];w[tot].val=c;head[a]=tot; } void dfs(int x) {id[++cnt]=x;pos[x]=cnt;for(int i=head[x];i;i=w[i].nextt){int v=w[i].to;if(v==f[x][0])continue;f[v][0]=x;valuee[v]=valuee[x]+w[i].val;for(int i=1;i<=20;++i)f[v][i]=f[f[v][i-1]][i-1];dep[v]=dep[x]+1;dfs(v);} } int lca(int x,int y) {if(dep[x]<dep[y])swap(x,y);for(int i=20;i>=0;--i)if(dep[f[x][i]]>=dep[y])x=f[x][i];if(x==y)return x;for(int i=20;i>=0;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } LL dis(int x,int y) {LL res=0;int LCA=lca(x,y);res+=valuee[x]+valuee[y]-2*valuee[LCA];return res; } void modify(int x) {now[x]^=1;if(now[x]) s.insert(pos[x]);else s.erase(pos[x]);if(s.size()<=1){ans=0;return;}//存在只有一个和没有的情况,都要直接返回,这个时候就已经不存在前驱后继了 set<int>::iterator pre,nxt;pre=s.lower_bound(pos[x]);//大于等于nxt=pre;if(now[x])nxt++;if(pre==s.begin())pre=--s.end();else pre--;if(nxt==s.end()) nxt=s.begin();LL dis1=dis(id[*pre],id[*nxt]);LL dis2=dis(id[*pre],x);LL dis3=dis(x,id[*nxt]);if(now[x])ans+=dis2+dis3-dis1;else ans-=dis2+dis3-dis1; } int main() {int n=read(),m=read();for(int i=1;i<n;++i){int a=read(),b=read(),c=read();add(a,b,c);add(b,a,c);}dep[1]=1,dfs(1);for(int i=1;i<=m;++i){int x=read();modify(x);printf("%lld\n",ans);} }
BZOJ3991
然后看一下考试时遇到的有一点小变化的题
题面
初音未来的巡游
128MB / 1s ; cruise.cpp / c / pas / in / out
【题目描述】
Miku决定在n个地点中选择一些地点进行巡游。这n个地点由n-1条道路连接,两两之间有且仅有一条路径可以互相到达。Miku希望在这些道路中选择一些放上葱,使得Miku可以选择一种方式只经过有葱的道路而巡游完所有她选择的地点(一条道路可以被多次经过,起点任选)。
Miku想知道她至少需要准备多少葱。由于她的巡游计划可能更改,她希望你能在更改后即时回答她的疑问。
【输入格式】
第一行两个整数n,m,表示地点数和事件数。
第2至n行,每行两个整数x,y,表示地点x和地点y之间有一条无向道路。
接下来一行n个0/1数,若第i个数为1则表示i号地点初始时被选,为0则表示不选。
接下来一行m个整数,依次表示修改事件。第i个数Ai表示Ai号地点的状态发生变化,即若当前被选则改为不选,当前不选则改为被选。
【输出格式】
输出m行,第i行一个整数表示第i次事件后初音最少需要准备多少葱。
【样例数据】
cruise.in |
cruise.out |
5 8 1 2 1 3 2 4 2 5 1 0 0 1 0 5 4 2 1 2 5 3 2 |
3 2 2 1 0 0 0 2 |
【数据范围】
对于30%的数据,n,m≤3000。
对于另30%的数据,开始时所有地点都不选,保证修改操作的地点当前是不选状态。
对于100%的数据,1≤n,m≤200000,1≤x,y,Ai≤n。
不同的只是边权值全部为1,且只需要统计走过的路径条数,而不是走过的距离,简单分析我们可以发现,其实就是上一道题的ans/2,因为原来是走过了还要走回去,相当于走两次。
#include<bits/stdc++.h> #define N 200003 #define LL long long using namespace std; int read() {int x=0,f=1;char s=getchar();while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}return x*f; } struct EDGE{int to,nextt; }w[N*2]; int vis[N],tot=0,head[N],now[N],cnt=0; int f[N][23],dep[N],id[N],pos[N]; int ans=0; set<int>s; void add(int a,int b) {tot++;w[tot].to=b;w[tot].nextt=head[a];head[a]=tot; } bool dfs(int x) {id[++cnt]=x;pos[x]=cnt;bool back=now[x];for(int i=head[x];i;i=w[i].nextt){int v=w[i].to;if(v==f[x][0])continue;f[v][0]=x;for(int i=1;i<=20;++i)f[v][i]=f[f[v][i-1]][i-1];dep[v]=dep[x]+1;if(dfs(v))//说明fa为v这条路径是要放葱的,因为下面有要选择的点,而到那个点的路径唯一,必须经过fa为v这条路 {ans++;//放葱的路径数++ back=1;}}return back; } int lca(int x,int y) {if(dep[x]<dep[y])swap(x,y);for(int i=20;i>=0;--i)if(dep[f[x][i]]>=dep[y])x=f[x][i];if(x==y)return x;for(int i=20;i>=0;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } int dis(int x,int y) {int LCA=lca(x,y);return dep[x]+dep[y]-2*dep[LCA]; } void modify(int x) {now[x]^=1;if(now[x]) s.insert(pos[x]);else s.erase(pos[x]);if(s.size()<=1){ans=0;return;}//存在只有一个和没有的情况,都要直接返回 set<int>::iterator pre,nxt;pre=s.lower_bound(pos[x]);//大于等于 nxt=pre;if(now[x])nxt++;if(pre==s.begin())pre=--s.end();else pre--;if(nxt==s.end()) nxt=s.begin();int dis1=dis(id[*pre],id[*nxt]);int dis2=dis(id[*pre],x);int dis3=dis(x,id[*nxt]);if(now[x])ans+=dis2+dis3-dis1;else ans-=dis2+dis3-dis1; } int main() {freopen("cruise.in","r",stdin);freopen("cruise.out","w",stdout);int n=read(),m=read();for(int i=1;i<n;++i){int a=read(),b=read();add(a,b);add(b,a);}int go=0;for(int i=1;i<=n;++i){now[i]=read();if(now[i])go=i;}if(!go)dep[1]=1,dfs(1);else dep[go]=1,dfs(go);//从一个被选择的点开始走一定更优 ans=ans*2;for(int i=1;i<=n;++i)if(now[i]) s.insert(pos[i]);for(int i=1;i<=m;++i){int x=read();modify(x);printf("%d\n",ans/2);} }
View Code
转载于:https://www.cnblogs.com/yyys-/p/11260981.html
最小行走距离(dfs+虚树)相关推荐
- Jittery Roads Gym - 100889J (虚树 + DP + dfs 序, + 线段树)
每次给一个点集, 求每个点到其他所有点的最大距离: 会修改边权; 修改边权之后, 我们可以用 dfs 序 + 线段树维护 当前点到根节点的距离. 还可以用树状数组 + 差分思想 维护. { dfs 序 ...
- BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】
题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...
- 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)
题目链接:点击查看 题目大意:给出一个无穷个节点的树,对于每个大于 1 的点 i 来说,可以向点 i / minvid[ i ] 连边,这里的 mindiv[ x ] 表示的是 x 的最小质因数,现在 ...
- BZOJ2286: [Sdoi2011]消耗战(虚树)
BZOJ2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成, ...
- LOJ 2339 「WC2018」通道——边分治+虚树
题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...
- bzoj 3611: [Heoi2014]大工程(虚树+树形DP)
3611: [Heoi2014]大工程 Time Limit: 60 Sec Memory Limit: 512 MB Submit: 1697 Solved: 718 [Submit][Stat ...
- 虚树(bzoj 3572: [Hnoi2014]世界树)
例题: 一棵n个节点的树,m次查询,每次查询给你一个点集U,对于树上的所有节点x(x∉U),你要找到一个点y(y∈U)满足y点离x点最近且标号最小,表示x点受y点管辖,而你的任务就是对于每次查询输出U ...
- 算法 | 虚树学习笔记
虚树学习笔记? blog 虚树是一棵虚拟构建的树-废话 这棵树只包含关键点和关键的点,而其他不影响虚树结构的点和边都相当于进行了路径压缩-而且整棵虚树的大小不会超过关键点的2倍 举个例子? 比方说-4 ...
- 【洛谷P6199 [EER1]河童重工】【点分治+虚树】
题意 给出两棵树T1,T2T_1,T_2T1,T2,定义新图中两个点的距离为dis1(i,j)+dis2(i,j)dis_1(i,j)+dis_2(i,j)dis1(i,j)+dis2(i,j ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...
最新文章
- 常用数学符号读法大全
- JQUERY学习第二天之制作横纵向导航菜单
- python61到08使用说明书_python 08 文件操作
- 2021年国货彩妆品牌推广营销趋势
- 37 FI配置-财务会计-固定资产-组织结构-复制参考折旧表/折旧范围表
- 《数学建模与数学实验》第5版 线性规划 习题3.4
- 《图书管理系统》—需求分析报告
- 使用Mediacoder压制带有图片的ass字幕
- Java开发之HTTP协议详解
- windows开机自启exe程序bat
- Memcached安全基线
- 红警ol服务器维护中1003,红警OL频繁掉线怎们么办 网络断开连接解决办法
- 22考研全年备考规划表,这5个时间点你必须知道!
- paper 94:视觉领域博客资源1之中国部分
- Multipathd Daemon was Unable to Set Options fast_io_fail_tmo or dev_loss_tmo Under UEK1 or RHCK
- gitlab解决permission deny问题
- 全国计算机一级第七套试题及答案,计算机一级考试第七套试卷及答案.doc
- 今天没有带U盘,把代码拷到网上再回家贴
- iPhone中取得LAC和cellid等信息
- Swift 通知推送新手指南
热门文章
- 分词统计(四)唐宋元诗人吟诗作词的时候,最偏爱哪些词语呢?(附上AI写的1000句诗!)
- Spring Transaction : TransactionInterceptor
- 新浪微博开放平台开发-android客户端(1)
- VSCode Workspace使用,以及file.exclude、search.exclude的使用模板
- excel冻结窗口_冻结窗口怎么冻结多行
- lc滤波器是利用电感的感抗_由浅入深讲解滤波电路工作原理
- 图片透明底如何在线生成?试试小编推荐的这款工具吧
- 【Python数据分析】二手车价格预测
- TARA-威胁建模方案4
- 飘窗利用如何改造 6个实用设计包你满意