树的直径

树的直径

树的直径性质

摘自oi-wiki。

定理:在一棵树上,从任意节点 \(y\) 开始进行一次 DFS,到达的距离其最远的节点 \(z\) 必为直径的一端。

(证明通过反证法即可)(注意此定理建立在边权为正的基础上)

树的直径求法

由上面的定理诞生了一种求法:通过两次 \(dfs\) 找最远点即可。

另一种则是树形 \(dp\)(对链分类讨论即可)。

树形 \(dp\) 实现:

vector v[2005];

int dp[2005],ans;

void dfs(int x,int fa){

dp[x]=0; int max1=0,max2=0;

for(int i:v[x])

if(i!=fa){

dfs(i,x); int mm=dp[i]+1;

if(mm>=max1)max2=max1,max1=mm;

else if(mm>=max2)max2=mm;

}

dp[x]=max1,ans=max(ans,max1+max2);

}

一般来说,树形 \(dp\) 码量(其实都很少)和适用范围都较两次 \(dfs\) 更优。

难道上面的定理纯废物吗?非也。

例题

城镇

L国一共有N座城镇,开始时它们两两不连通。L国计划依次建造N-1条道路,把所有城镇连通起来。

每建完一条道路,你需要回答这条道路所在连通块内距离最远的两座城镇之间的距离。

N≤300000

不难想到分类讨论:

合并两个联通块时,

直径要么在原子树中,要么经过新建的边(废话)。

第一种情况可以将信息存在并查集根节点上,

第二种情况可以考虑维护原子树的最长根链,将其合并即可。

你想到了什么?对,就是上面的定理!直径的端点之一就是最长根链的另一端。

这个也可以用并查集根节点存储(万能的根节点,联通块信息都可以存)。

代码实现:

#include

#include

#include

using namespace std;

constexpr int N=3e5+5;

int n,a1[N],a2[N];

vector v[N];

int fa[N],son[N],siz[N],dep[N],top[N];

int e1[N],e2[N];

struct DisJoint_Set{

int f[N];

void clear(){ for(int i=1;i<=n;i++)f[i]=i; }

int find(int x){ return (x==f[x])?x:f[x]=find(f[x]); }

void merge(int x,int y){

if(dep[find(x)]>dep[find(y)])f[find(x)]=find(y);

else f[find(y)]=find(x);

}

}s;

void dfs1(int x,int ffa){

fa[x]=ffa,dep[x]=dep[ffa]+1,siz[x]=1;

for(int i:v[x])if(i!=ffa){

dfs1(i,x),siz[x]+=siz[i];

if(siz[son[x]]

}

}void dfs2(int x,int t){

top[x]=t; if(son[x])dfs2(son[x],t);

for(int i:v[x])if(i!=fa[x]&&i!=son[x])dfs2(i,i);

}int LCA(int x,int y){

while(top[x]!=top[y]){

if(dep[top[x]]

x=fa[top[x]];

}return (dep[x]

}struct Cho{

int e1,e2,len;

void get_len(){

int lca=LCA(e1,e2);

len=dep[e1]+dep[e2]-2*dep[lca];

}void insert(int x,int y){

e1=x,e2=y,get_len();

}bool operator <(const Cho &x)const{

return len>x.len;

}

}a[10];

signed main(){

ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

cin>>n; s.clear();

for(int i=1;i

cin>>a1[i]>>a2[i],

v[a1[i]].push_back(a2[i]),

v[a2[i]].push_back(a1[i]);

dfs1(1,0),dfs2(1,1);

for(int i=1;i<=n;i++)e1[i]=e2[i]=i;

for(int i=1;i

int x=a1[i],y=a2[i]; x=s.find(x),y=s.find(y);

a[1].insert(e1[x],e2[x]); a[2].insert(e1[y],e2[y]);

a[3].insert(e1[x],e2[y]); a[4].insert(e1[y],e2[x]);

a[5].insert(e1[x],e1[y]); a[6].insert(e2[x],e2[y]);

sort(a+1,a+7); s.merge(x,y); x=s.find(x);

e1[x]=a[1].e1,e2[x]=a[1].e2; cout<

}return 0;

}

相关文章

手机爱奇艺投屏到电视的简易操作指南与注意事项
365bet注册送35元

手机爱奇艺投屏到电视的简易操作指南与注意事项

📅 09-22 👀 8029
深度揭秘善心汇幕后运作方式 竟是一场骗局
365bet注册官网

深度揭秘善心汇幕后运作方式 竟是一场骗局

📅 08-13 👀 7397
世界任務
365提款不到账的吗

世界任務

📅 10-30 👀 3736