首页 > 其他分享 >树的直径

树的直径

时间:2024-08-21 21:28:51浏览次数:13  
标签:int dfs fa 直径 d1 dis

树的直径

树上任意两节点之间最长的简单路径即为树的直径

即树上最长的链

显然树可以有多条直径

SPOJ PT07Z

模版

树的直径两种求法,时间复杂度均为\(O(n)\)

常用的是两遍dfs

第一遍dfs从任一点开始,找到可以到达的最远点,这个最远点就是直径的一个端点,第二遍dfs再从这一端点出发,再一次去找可以到达的最远点,这个点就是直径的另一端点

正确性证明

void dfs(int i,int fa){
	f[i]=fa,maxdep[i]=dis[i];
	if(dis[i]>maxn){
		maxn=dis[i];
		id=i;
	}
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v;
		if(v==fa||flg[v])continue;
		dis[v]=dis[i]+1;
		dfs(v,i);
		maxdep[i]=max(maxdep[i],maxdep[v]);
	}
}
void gettree(){
	maxn=-1;
	dfs(1,0);
	int beginpoint=id;
	maxn=-1,dis[bp]=0;
	dfs(bp,0);
	int endpoint=id;
}

缺点是不可用于含有负权的树

第二种方法是dp

维护每个点向下延伸的最长距离\(d1[i]\)和\(d2[i]\),树的直径的长度即为\(max\{d1[i]+d2[i]\}\)

void dfs(int i,int fa){
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v;
		if(v!=fa){
			dfs(v,i);
			if(d1[v]+1>d1[i]){
				d2[i]=d1[i];
				d1[i]=d1[v]+1;
			}else if(d1[v]+1>d2[i]){
				d2[i]=d1[v]+1;
			}
		}
	}
	ans=max(ans,d1[i]+d2[i]);
}

可以用于含有负权的树

P1099&P2491

在直径上找到一段不超过限制长度的路径\(F\),使得\(F\)外节点到\(F\)的最大距离(偏心距)最小

比较暴力的做法是,先求出直径,然后分别从直径上的每个点开始,沿直径延伸到达最远的点,这样确定了路径\(F\),然后dfs统计答案。确定路径\(F\)时可以使用双指针法,这样时间复杂度为\(O(n^2)\)。实际上,偏心距只能是直径的两端点到路径\(F\)两端的距离较大者,设为\(x\),或者直径上的一点不经过直径上的点所到达的最远距离,设为\(y\)。前者显然,所以计算偏心距时,对于双指针找出的每一段路径\(F\),偏心距取所有\(x\)的距离的最小值,其他情况必然更劣,然后dfs与所有\(y\)取最大值。

可能疑惑:是否可能存在一条\(y\),它在直径上的端点不在所选取的\(F\)上,导致所得的偏心距偏小,实际上是不可能的,这样违背了直径最长的性质。最终的时间复杂度是\(O(n)\)

void dfs(int i,int fa){
	f[i]=fa;
	if(d[i]>maxn){
		maxn=d[i];
		id=i;
	}
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v,w=e[j].w;
		if(v!=fa&&!vis[v]){
			d[v]=d[i]+w;
			dfs(v,i);
		}
	}
}
int main(){
	memset(head,-1,sizeof(head));
	n=read(),s=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),w=read();
		addedge(u,v,w);addedge(v,u,w);
	}
	int bp,ep;
	dfs(1,0);
	bp=id;
	d[bp]=0;maxn=0;
	dfs(bp,0);
	ep=id;
	for(int i=ep,j=ep;i;i=f[i]){//双指针
		while(d[j]-d[i]>s)j=f[j];
		ans=min(ans,max(d[i],d[ep]-d[j]));
	}
	for(int i=ep;i;i=f[i]){//这个for不要和下面的for写到一起
		vis[i]=1;
	}
	for(int i=ep;i;i=f[i]){
		d[i]=0;
		dfs(i,f[i]);
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,d[i]);
	}
	cout<<ans<<"\n";
	return 0;
}

P4408

可以知道,其中一条路径是树的直径,那么考虑另一条路径的选取,设该路径为\(F\),可以知道\(F\)的一个端点是直径的一个端点,于是求出所有点到直径两个端点的距离\(d1\)和\(d2\),根据题目描述,各个点所能的贡献最长距离\(d[i]=min\{d1[i],d2[i]\}\),路径\(F\)的长度为\(max\{d[i]\}\)

long long

P5536

找一个大小为\(k\)的联通块,使联通块之外点到联通块的最大距离最小

与树网的核不同,\(k\)可能大于直径

易知树的直径的中点是必选的,因为直径中点到所有叶子的最大距离最小(否则违背了直径最长性质),如果不选一定更劣。由此,从直径中点dfs计算每个点的深度\(dep[i]\)和可以到达的最大距离\(maxdep[i]\),按照\(maxdep[i]-dep[i]\)排序,优先选择较大者

void dfs(int i,int fa){
	f[i]=fa,maxdep[i]=dis[i];
	if(dis[i]>maxn){
		maxn=dis[i];
		id=i;
	}
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v;
		if(v==fa||flg[v])continue;
		dis[v]=dis[i]+1;
		dfs(v,i);
		maxdep[i]=max(maxdep[i],maxdep[v]);
	}
}
int main(){
	memset(head,-1,sizeof(head));
	n=read(),k=read();	
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		addedge(u,v);addedge(v,u);
	}
	maxn=-1;
	dfs(1,0);
	int bp=id;
	maxn=-1,dis[bp]=0;
	dfs(bp,0);
	int ep=id;
	int mid=0;
	for(int j=0,i=ep;j<=dis[ep]/2;j++,i=f[i])
		mid=i;
	dis[mid]=0;
	dfs(mid,0);
	for(int i=1;i<=n;i++)
		maxdep[i]=maxdep[i]-dis[i];
	sort(maxdep+1,maxdep+n+1);
	for(int i=1;i<=n-k;i++)
		ans=max(ans,maxdep[i]);
	cout<<ans+1<<"\n";
	return 0;
}

标签:int,dfs,fa,直径,d1,dis
From: https://www.cnblogs.com/wertyuio1/p/18372579

相关文章

  • AcWing 1078. 旅游规划 (DFS找树的直径+直径中点性质求解,无DP)
    原题链接题目描述算法引用自树的直径-OI-Wiki:若树上所有边边权均为正,则树的所有直径中点重合证明:使用反证法。设两条中点不重合的直径分别为\(\delta(s,t)与\delta(s',t')\),中点分别为\(x\)与\(x'\)。显然,\(\delta(s,x)=\delta(x,t)=\delta(s',x')=\delta(......
  • 『树的直径、重⼼』Day10
    DrazilandMorningExercise\(f\)可以换根求。对于一段乱序序列,你不好求其中max-min的限制。根据重心的性质,如果你让重心为root,那么向下\(f\)一定单减。这样,你就对每个点在末尾的情况,树上倍增找到最大的点,树上差分即可。现在想到了好像可以线段树合并,那么你当前点就......
  • 【题解】Solution Set - NOIP2024集训Day10 树的直径、重⼼、中⼼
    【题解】SolutionSet-NOIP2024集训Day10树的直径、重⼼、中⼼https://www.becoder.com.cn/contest/5464「CF516D」DrazilandMorningExercise首先,我们可以换根求出所有点的\(f\)。然后不会了……思考一下,一条直径提供的到底时什么。实际上,一条直径上的点取到\(f\)......
  • 【算法模板】计算几何:旋转卡壳求凸包直径
    旋转卡壳算法是一种几何算法,主要用于在二维平面上求解与凸包相关的最优问题。该算法利用凸包顶点的顺序性和对称性,通过模拟两个卡壳(calipers)沿着凸包边界的旋转来寻找最优解。常见的应用包括计算凸包的直径(即最远点对之间的距离)、最小包围矩形(最小面积矩形),以及最小宽度(宽度......
  • 【树的直径 求树中距离跟阶段点最远的点】CodeForce1822F.md
    CF1822F-Problem-F-Codeforces题目大意:无根树的每条边为k,定义操作:移动根节点为把当前的根ROOT移动到相邻节点,每次代价为c,定义成本=从ROOT出发到达的最长的路径的长度,利润=成本-代价,求利润最大值\[\begin{align}&\huge\color{red}记得开\text{longlong}\\\\\\&\huge思......
  • 浅谈图论中树及其相关知识点(树的遍历、直径、最近公共祖先)(c++)
    目录前言一.关于树二.树的遍历(一)遍历方式常见遍历1.DFS遍历2.BFS遍历二叉树遍历1.先序遍历2.中序遍历3.后序遍历(二)例题讲解1.P1030[NOIP2001普及组]求先序排列思路AC代码 2.P5908猫猫和企鹅思路AC代码  3.P1395会议思路AC代码三.树的直径(一)定......
  • P3304 [SDOI2013] 直径
    原题链接题解先随便找一条直径,然后标记这些边,然后看看直径上的点有没有不需要经过标记边的路径,使得其长度等于该点到直径端点的路径长度code#include<bits/stdc++.h>#definelllonglongusingnamespacestd;structedge{llto,val;};vector<edge>G[200005];l......
  • 树的直径
    树的直径可能不是唯一的,但树的中点是唯一的,我的意思是..U81904【模板】树的直径有以下两种办法求得树的直径:两遍DFS(或BFS):可以记录点树形DP:允许有负边权首先是两遍DFS可求路径点击查看代码inthead[N],cnt;structEdge{intfrom,to,nxt;llval;......
  • CF911F-构造、树直径
    link:https://codeforces.com/contest/911/problem/F给一棵树,你需要进行若干次操作:选择两个叶子,把他们的距离加入得分,删掉其中一个叶子。希望让最终得分最大。构造方案。删叶子,距离最大,考虑树的直径很明显用树的直径不会让答案更劣(一棵树可能有多个直径,但直径的中心是唯一的,在......
  • CF1943C-构造、树直径
    link:https://codeforces.com/contest/1943/problem/C题意:给一棵树,初始所有点为白色,每次操作可以选一个点\(v\),和一个距离\(d\),表示将所有距离点\(v\)恰好\(d\)的点染成黑色,问最少需要几次操作让树全黑,给出操作序列。树、二分图、黑白染色一条链怎么做?\(s_1,\dots,s_n\)......