总结:树的中心
Ak_hjc_using · · 算法·理论
(注:本文为作者的真实学习情况,没有借鉴与抄袭他人的内容,制作不易,点个赞支持一下吧)。
\texttt{总结:树的中心}
\texttt{前置知识:} 树的直径
- 树的直径的性质
假设边权非负,那么树的直径的端点则一定在度为
1 的点上。如果边权取任意值(也就是说可为负数),那么任意一点到达直径上的一点的距离一定最短。
Code(使用的是树形 DP 求取的树的直径)。
树形 DP 求取树的直径的优势是可以直接处理所有的边权,但是缺点是端点不易获取。
-
\texttt{定义}
树的中心定义:在一棵树中(无根树),如果将节点
推导出性质:将刚好把直径一分为
-
\texttt{性质}
-
树的中心不一定唯一,但最多有
2 个,且这两个中心是相邻的。 -
树的中心一定位于树的直径上。
-
树上所有点到其最远点的路径一定交会于树的中心。
-
当树的中心为根节点时,其到达直径端点的两条链分别为最长链和次长链。
-
当通过在两棵树间连一条边以合并为一棵树时,连接两棵树的中心可以使新树的直径最小。
-
树的中心到其他任意节点的距离不超过树直径的一半。
推广性质:所有的直径都相交于树的中心。
-
\texttt{求法}
此处给出两种树的中心的求法。
-
使用 dfs 求解,由于是无根树,直接枚举根节点,然后使用 dfs 直接求取树的直径,时间复杂度为
O(n\log n) 。 -
使用树形 DP(换根 DP) 算法求解。
由于使用 dfs 算法十分简单,故只给出换根 DP 的详细做法:
寻找一个点
\texttt{具体步骤}
- 维护数组
down1_x ,表示以x 作为子树的根节点时的最长链的长度。
简化意思:记录点
- 维护数组
down2_x ,表示
简化意思:记录点
-
维护数组
up_x ,表示节点x 的子树外的最长链,此处有一性质:即该链必将经过x 的父亲节点。 -
那么最终的点
x 如果使得\max(down1_x,_up_x) 最小,那么x 即为树的中心。
\texttt{实现步骤} :
- 利用树的中心的性质,直接找到每个点的最长链的最小值。
但是,此处出现了一个问题,就是维护的
-
求解所有的
down1_x 与down2_x ,直接使用这两个数组来直接求取up_x ,即可避免提根的时间复杂度。 -
只需要求解所有的
up_x 即可得到答案。
故现在使用换根 DP 的基本思路已经说完,开始使用代码实现上面的思路。
补充(换根 DP 的基本步骤)
步骤 1:任意选取一个根节点。
步骤 2:dfs 的过程之中,统计出当前子树内的节点对当前节点的贡献。
步骤 3:再来一次 dfs,统计出当前节点的父节点对当前节点的贡献,然后合并统计答案。
Code。(换根 DP)
综上所述,使用换根 DP 做法的时间复杂度为
\texttt{例题讲解}
\texttt{U392706【模板】树的中心}
题目描述:
给定一棵
思路:
本题为树的中心模板题,直接使用树的中心(换根 DP)做法直接求取即可。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,down1[N],down2[N],up[N],pre[N];
struct node
{
int v;
int w;
};
vector<node>G[N];
void dfs1(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
if(v!=fa)
{
dfs1(v,u);
int x=down1[v]+w;
if(down1[u]<x)down2[u]=down1[u],down1[u]=x,pre[u]=v;
else if(down2[u]<x)down2[u]=x;
}
}
}
void dfs2(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
if(v!=fa)
{
if(pre[u]==v)up[v]=max(down2[u],up[u])+w;
else up[v]=max(down1[u],up[u])+w;
dfs2(v,u);
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
dfs1(1,0);
dfs2(1,0);
int res=1e9;
for(int i=1;i<=n;i++)res=min(res,max(down1[i],up[i]));
for(int i=1;i<=n;i++)if(res==max(down1[i],up[i]))cout<<i<<'\n';
return 0;
}