浅谈树的直径
定义:
树的直径指树上最长链(最远点对)
求解:
树的直径存在两种求解方式均为O(n)复杂度,其各有优劣
1.贪心法
任取一点作为起点,找到树上距离该点的最远点,记作st,再以st为起点,找到树上距离st最远的点,记作ed,st至ed即为树的直径。
(找最远点操作DFS和BFS均可)
优点:起点与终点方便获得。
缺点:负边权就GG。
inline void dfs(int now,int fa,int deep)
{
if(deep>res)
{
res=deep;
ed=now;
}
f[now]=fa;
dep[now]=deep;
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(t==fa) continue;
dfs(t,now,deep+a[i].val);
}
}
dfs(1,0,0);
res=0;
st=ed;
dfs(st,0,0);
2.树型DP
任取一点作为起点,记录树上每一点向下的最远距离和非严格次远距离,直径长度即为每一点二者之和的最大值。
优点:能处理负边权。
缺点:起点终点难以记录。
inline void dfs(int now,int fa)
{
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(t==fa) continue;
dfs(t,now);
res=max(res,dp[now]+dp[t]+a[i].val);
dp[now]=max(dp[now],dp[t]+a[i].val);
}
}
性质:
1.直径两端点一定是叶子节点。
2.距任意点最远点一定是直径的端点,据所有点最大值最小的点一定是直径的中点。
3.两棵树相连,新直径的两端点一定是原四个端点中的两个
4.两棵树相连,新直径长度最小为max(max(直径1,直径2),半径1+半径2+新边长度 ) (设k为直径中最接近中点的节点,半径=max(tot-d[k],d[k]))
5.一棵树上接一个叶子结点,直径最多改变一个端点
6.若一棵树存在多条直径,多条直径交于一点,且交点是直径的严格中点(中点可能在某条边内)
例题:
前言:树的直径姿势点主要考察各种性质的应用,灵活运用各种性质即可切题。
题目概述:有一棵树,要求我们在树上加上1~2两条边,使遍历每一条路时经过的路径最短,关于路径,有以下几个要求
1.新建的边必须正好经历一次
2.新边可以是自环
3.遍历后回到起点
分析:既然要回到起点,在无环情况下,每条边必然要走两遍,路径长度为2*n-2;
对于k=1的情况,一个贪心的想法必然是让树上最长边形成一个环,所以求一遍树的直径即可,设树的直径长度为res,路径长度变为2*n-2-res+1;
而对于k=2的情况,在加完第一条边后,必然要找第二条最长的边
然而在找第二条最长边的时候我们发现:若第二条边与第一条边有重叠部分,那么重叠部分就要再走一次,相当于减少了第一次的贡献
同时,由于建立新边是为了防止一条边被第二次遍历,第一条最长边选中的部分本身遍不用第二次遍历,所以该边对于最长边的贡献本就应是零
综合本来的贡献和减少的第一次贡献,改变对于第二次求最长边的贡献应该是边权的负值XD,此时我们应该将第一条最长边的边权全部取反,再找一条最长边。
在取反后,树上遍有了负边权,我们需要采取树型DP求第二条最长边,而且由于需要取反第一条最长边,我们需要第一条最长边的路径,第一条适合用dfs求最长边
总结:该题目综合考察了DFS求树的直径和树型DP求树的直径的优点。
题目概述:在一颗树上选取一段区间,长度小于m,使得所有节点到该区间的最大值最小。
分析:由性质2:据任意点最大值最小的点一定是直径的中点 可知:中点必然入选。
再考虑将中点扩展为区间:据任意点最远点一定是直径的中点可知,若扩展为区间,在直径上扩展一定是最优的
可反证:若存在另一点到直径上一点距离大于直径端点到直径上一点距离,那么将会产生新的直径。
现在问题转化为在直径上选取一段区间长度小于m,使得任意一点到这段区间的最大值最小;
我们可以预处理出其他节点到达直径的最远距离,一次为l,以直径长度为r,二分答案(判断根据性质2只需判断该区间到直径端点距离是否小于mid)
近一步我们可以双指针法直接求答案
注意一个特判:若m>=直径长度,直接输出l。
总结:重点考察了性质2和相关应用。
题目概述:在树上断开一条边连到别处,使得新树的直径最小,求直径最小值。
分析:为了改变新树直径,断开的边必然在原树直径上,找到一条直径,枚举断开哪一条边。(若有多条直径也没法管)
根据性质4:新树直径最小值为max(max(子树1直径,子树2直径),子树1半径+子树2半径+断开边长度),在所有新树直径中取最小值即可。
总结:考察性质4运用。
题目概述:给定一棵树,求所有直径都共同经过的边的数量。
分析:根据性质6:所有直径必然交于中点,我们只要分析中点在边内和在节点上的情况。
情况1:中点在边内:ans=1,对于该边的两个节点做一次以下操作:
处理出每个点能够到达的最大深度,记作d[now],若存在0个或1个以上的子节点满足d[t]+边权==d[now]则该节点向下不存在直径必经边。
若仅存在1个子节点d[t]+边权==d[now],++ans,再对该子节点做一个相同操作。
情况2:中点在点上:处理出每个点能够到达的最大深度,扔进堆里,取前三大的点比较,设为a,b,c。
若不存在c或a=b>c,ans=2,a,b,做一次上述操作。
若a=b=c,则不存在必经边。
该题目略难以理解(至少弱鸡作者这么认为的QWQ),所以将代码放上,但是由于各大OJ该题数据普遍过水,不存在中点在节点上的数据,作者并不能保证代码的正确性,若有大神发现不妥之处欢迎指正
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> p;
priority_queue<p> q;
inline int read()
{
int x=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
int n,st,res,tot,now,ans;
int f[200010],dep[200010],d[200010];
int head[200010],cnt;
struct point
{
int nxt,to,val;
}a[500010];
inline void add(int x,int y,int z)
{
a[++cnt].nxt=head[x];
a[cnt].to=y;
a[cnt].val=z;
head[x]=cnt;
}
inline void dfs(int now,int fa,int deep)
{
f[now]=fa;
dep[now]=deep;
d[now]=0;
if(deep>res)
{
res=deep;
st=now;
}
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(t==fa) continue;
dfs(t,now,deep+a[i].val);
d[now]=max(d[now],d[t]+a[i].val);
}
}
inline void work(int x,int y)
{
dfs(x,y,0);
while("miao")
{
int t=0,sum=0;
for(int i=head[x];i;i=a[i].nxt)
{
if(a[i].to==y) continue;
if(d[x]==d[a[i].to]+a[i].val)
{
t=a[i].to;
++sum;
}
}
if(sum^1) break;
y=x;
x=t;
++ans;
}
}
signed main()
{
n=read();
for(int x,y,z,i=1;i<n;++i)
{
x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
dfs(1,0,0);
res=0;
dfs(st,0,0);
printf("%lld\n",res);
while(2*dep[f[st]]>=res) st=f[st];
if(2*dep[st]==res)
{
dfs(st,0,0);
for(int i=head[st];i;i=a[i].nxt)
{
int t=a[i].to ;
q.push(p(d[t]+a[i].val,t));
}
p a=q.top();
q.pop();
p b=q.top();
q.pop();
if(!q.empty())
{
ans=2;
work(a.second,st);
work(b.second,st);
}
else
{
p c=q.top();
if(c.first==a.first)
{
ans=0;
}
else
{
ans=2;
work(a.second,st);
work(b.second,st);
}
}
}
else
{
ans=1;
work(st,f[st]);
work(f[st],st);
}
printf("%lld\n",ans);
return 0;
}
总结:考察性质6以及直径中点相关知识。
来源:https://www.cnblogs.com/knife-rose/p/11189707.html