好吧,感觉标题的名字取得有一点奇葩呢!源于我的高度概括

所以再做一些具体放阐述,这个问题大概意思是:在树上指定了一些点,你可以从树上的任意点出发,求走完所有指定点的最小行走距离(可存在一条边重复走,走几次这条边的权值就加几次)。

这个问题呢,我是在训练考试中碰到的,但是题目和要求做过一些小修改,为了更好的理解这种思想,我搜索到了这样一道最原始的题。

[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+虚树)相关推荐

  1. Jittery Roads Gym - 100889J (虚树 + DP + dfs 序, + 线段树)

    每次给一个点集, 求每个点到其他所有点的最大距离: 会修改边权; 修改边权之后, 我们可以用 dfs 序 + 线段树维护 当前点到根节点的距离. 还可以用树状数组 + 差分思想 维护. { dfs 序 ...

  2. BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】

    题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...

  3. 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)

    题目链接:点击查看 题目大意:给出一个无穷个节点的树,对于每个大于 1 的点 i 来说,可以向点 i / minvid[ i ] 连边,这里的 mindiv[ x ] 表示的是 x 的最小质因数,现在 ...

  4. BZOJ2286: [Sdoi2011]消耗战(虚树)

    BZOJ2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成, ...

  5. LOJ 2339 「WC2018」通道——边分治+虚树

    题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...

  6. bzoj 3611: [Heoi2014]大工程(虚树+树形DP)

    3611: [Heoi2014]大工程 Time Limit: 60 Sec  Memory Limit: 512 MB Submit: 1697  Solved: 718 [Submit][Stat ...

  7. 虚树(bzoj 3572: [Hnoi2014]世界树)

    例题: 一棵n个节点的树,m次查询,每次查询给你一个点集U,对于树上的所有节点x(x∉U),你要找到一个点y(y∈U)满足y点离x点最近且标号最小,表示x点受y点管辖,而你的任务就是对于每次查询输出U ...

  8. 算法 | 虚树学习笔记

    虚树学习笔记? blog 虚树是一棵虚拟构建的树-废话 这棵树只包含关键点和关键的点,而其他不影响虚树结构的点和边都相当于进行了路径压缩-而且整棵虚树的大小不会超过关键点的2倍 举个例子? 比方说-4 ...

  9. 【洛谷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 ...

  10. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

最新文章

  1. 常用数学符号读法大全
  2. JQUERY学习第二天之制作横纵向导航菜单
  3. python61到08使用说明书_python 08 文件操作
  4. 2021年国货彩妆品牌推广营销趋势
  5. 37 FI配置-财务会计-固定资产-组织结构-复制参考折旧表/折旧范围表
  6. 《数学建模与数学实验》第5版 线性规划 习题3.4
  7. 《图书管理系统》—需求分析报告
  8. 使用Mediacoder压制带有图片的ass字幕
  9. Java开发之HTTP协议详解
  10. windows开机自启exe程序bat
  11. Memcached安全基线
  12. 红警ol服务器维护中1003,红警OL频繁掉线怎们么办 网络断开连接解决办法
  13. 22考研全年备考规划表,这5个时间点你必须知道!
  14. paper 94:视觉领域博客资源1之中国部分
  15. Multipathd Daemon was Unable to Set Options fast_io_fail_tmo or dev_loss_tmo Under UEK1 or RHCK
  16. gitlab解决permission deny问题
  17. 全国计算机一级第七套试题及答案,计算机一级考试第七套试卷及答案.doc
  18. 今天没有带U盘,把代码拷到网上再回家贴
  19. iPhone中取得LAC和cellid等信息
  20. Swift 通知推送新手指南

热门文章

  1. 分词统计(四)唐宋元诗人吟诗作词的时候,最偏爱哪些词语呢?(附上AI写的1000句诗!)
  2. Spring Transaction : TransactionInterceptor
  3. 新浪微博开放平台开发-android客户端(1)
  4. VSCode Workspace使用,以及file.exclude、search.exclude的使用模板
  5. excel冻结窗口_冻结窗口怎么冻结多行
  6. lc滤波器是利用电感的感抗_由浅入深讲解滤波电路工作原理
  7. 图片透明底如何在线生成?试试小编推荐的这款工具吧
  8. 【Python数据分析】二手车价格预测
  9. TARA-威胁建模方案4
  10. 飘窗利用如何改造 6个实用设计包你满意