1.定义
如果在树中选择某个节点并删除,这棵树将分为若干棵子树,统计子树节点数并记录最大值。取遍树上所有节点,使此最大值取到最小的节点被称为整个树的重心。
(这里以及下文中的「子树」若无特殊说明都是指无根树的子树,即包括「向上」的那棵子树,并且不包括整棵树自身。)
用通俗一点的语言,就是将这个点去掉后,剩下的最大子树最小
2.性质
1.若树的重心不唯一,那么最多有两个,并且两个重心相邻
2.以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。
3.树中所有点到某个点的距离和中,到重心的距离和是最小的;
如果有两个重心,那么到它们的距离和一样。
4.把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
5.在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
3.实现方式
可以用深搜去实现,去统计一个点以此点为根节点的子树的大小,那么总结点数-当前节点总数就是剩余节点总数,然后去让最大的值最小
4.例题
P1395 会议
思路:带权树的重心板题,直接用换根dp去处理即可,跑两边dfs
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int u,v;
vector<int> e[200005];
int sz[200005];
int ans[200005];
int dp[200005];
void dfs1(int v,int fa)
{
sz[v]=1;
for(int u:e[v])
{
if(u!=fa)
{
dfs1(u,v);
sz[v]+=sz[u];
}
}
}
void get(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
get(u,v);
ans[v]+=ans[u]+sz[u];
}
}
}
void dfs2(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
dp[u]=dp[v]+(n-sz[u])-sz[u];
dfs2(u,v);
}
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n-1;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,-1);
get(1,-1);
dp[1]=ans[1];
dfs2(1,-1);
int maxn=0x3f3f3f3f,flag=0;
for(int i=1;i<=n;i++)
{
if(dp[i]<maxn)
{
flag=i;
maxn=dp[i];
}
}
cout<<flag<<" "<<maxn;
return 0;
}
P1364 医院设置
思路:和上面那个同理
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int w;
int u,v;
vector<int> e[200005];
int sz[200005];
int ans[200005];
int dp[200005];
int sum;
void dfs1(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
dfs1(u,v);
sz[v]+=sz[u];
}
}
}
void get(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
get(u,v);
ans[v]+=ans[u]+sz[u];
}
}
}
void dfs2(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
dp[u]=dp[v]+(sum-sz[u])-sz[u];
dfs2(u,v);
}
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w>>u>>v;
sum+=w;
sz[i]=w;
if(u!=0)
{
e[i].push_back(u);
}
if(v!=0)
{
e[i].push_back(v);
}
}
dfs1(1,-1);
get(1,-1);
dp[1]=ans[1];
dfs2(1,-1);
int minn=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
minn=min(minn,dp[i]);
}
cout<<minn;
return 0;
}
B. Kay and Snowflake
题意:就是去求,给你q个询问,每次询问以p为根节点的重心是哪个点
思路:我们由性质四可以知道,一个点的重心,一定在这个点到子节点的重心上,那么我们可以分析两种情况,子节点的重心是不是最大的联通量,上面的更大,那么我们将子节点重心上移更优,否则就是直接就是重心
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int u,v;
vector<int> e[300005];
int sz[300005];
int mx[300005];//去统计每个点的最大子树
int ans[300005];
int f[300005];
void dfs(int v,int fa)
{
sz[v]=1;
for(int u:e[v])
{
if(u!=fa)
{
dfs(u,v);
sz[v]+=sz[u];
mx[v]=max(mx[v],sz[u]);
}
}
int maxn=mx[v];//当前最大值为当前点的最大子树
ans[v]=v;
for(int u:e[v])
{
if(u!=fa)
{
int zhong=ans[u];
int shang=max(mx[zhong],sz[v]-sz[zhong]);
int k=zhong;
while(f[zhong]!=v)
{
zhong=f[zhong];//网上跳一步
int z=max(mx[zhong],sz[v]-sz[zhong]);
if(z<shang)
{
shang=z;
k=zhong;
}
else
{
break;
}
}
if(maxn>shang)
{
maxn=shang;
ans[v]=k;
}
}
}
}
signed main()
{
cin>>n>>q;
for(int i=2;i<=n;i++)
{
cin>>u;
f[i]=u;
e[u].push_back(i);
e[i].push_back(u);
}
dfs(1,-1);
for(int i=1;i<=q;i++)
{
cin>>v;
cout<<ans[v]<<"\n";
}
return 0;
}