树的重心
定义
对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心。
性质
- 树的重心如果不唯一,则至多有两个,且这两个重心相邻。
- 以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。
- 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
- 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
- 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
过程(?
围绕定义,我们 DFS 去寻找最大的子树的节点最少的点。
大体代码如下:
void dfs(int k,int fat){
siz[k]=1;
fa[k]=fat;
int mx=0;
for(int i=head[k];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat) continue;
dfs(v,k);
siz[k]+=siz[v];
mx=max(mx,siz[v]);
}
mx=max(mx,n-siz[k]);
if(mx<mn) {cen1=k;cen2=0;mn=mx;}
else if(mx==mn) {cen2=k;}
}
例题
可以发现,当只有一个重心时,删哪条边都行,删完再加上即可。
当有两个重心时,将一个重心的子树中的一个叶节点和它的父节点断开,再将这个叶节点和另一个重心连接,即可保证重心唯一。
code:
const int N=1e5+10;
int t,n,x,y,s,mn,cnt,cen1,cen2,fa[N],siz[N],head[N];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
void clean(){
cnt=0;
s=0;
mn=n+1;
cen1=cen2=0;
memset(fa,0,sizeof(fa));
memset(a,0,sizeof(a));
memset(siz,0,sizeof(siz));
memset(head,0,sizeof(head));
}
void dfs(int k,int fat){
siz[k]=1;
fa[k]=fat;
int mx=0;
for(int i=head[k];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat) continue;
dfs(v,k);
siz[k]+=siz[v];
mx=max(mx,siz[v]);
}
mx=max(mx,n-siz[k]);
if(mx<mn) {cen1=k;cen2=0;mn=mx;}
else if(mx==mn) {cen2=k;}
}
void Dfs(int x,int fat) {
int size=0;
for(int i=head[x];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat) continue;
size++;
}
if(size==0) {
s=x;
return ;
}
for(int i=head[x];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat) continue;
Dfs(v,x);
}
}
int main(){
t=read();
while(t--) {
n=read();
clean();
for(int i=1;i<n;++i) {
x=read();y=read();
add(x,y);
add(y,x);
}
dfs(1,0);
if(!cen2) {
cout<<1<<" "<<a[head[1]].v<<'\n'<<1<<" "<<a[head[1]].v<<'\n';
continue;
}
if(fa[cen1]!=cen2) swap(cen2,cen1);
Dfs(cen1,cen2);
cout<<s<<" "<<fa[s]<<'\n';
cout<<s<<" "<<cen2<<'\n';
}
return 0;
}
求出树的重心之后,再求出每个点到重心的距离。
注意要选择编号较小的重心。
code:
const int N=5e4+10;
int n,x,y,cnt,mn,cen1,cen2,dis[N],siz[N],head[N];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
void dfs(int x,int fat) {
int mx=0;
siz[x]=1;
for(int i=head[x];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat) continue;
dfs(v,x);
siz[x]+=siz[v];
mx=max(mx,siz[v]);
}
mx=max(mx,n-siz[x]);
if(mx<mn) {cen1=x;cen2=0;mn=mx;}
else if(mx==mn) {cen2=x;}
}
void Dfs(int x,int fat){
siz[x]=1;
for(int i=head[x];i;i=a[i].nxt) {
int v=a[i].v;
if(v==fat||v==0) continue;
Dfs(v,x);
siz[x]+=siz[v];
dis[x]+=dis[v]+siz[v];
}
}
int main(){
n=read();
mn=n+1;
for(int i=1;i<n;++i) {
x=read(); y=read();
add(x,y);
add(y,x);
}
dfs(1,0);
if(!cen2) {
cout<<cen1<<" ";
}
else {
cout<<min(cen1,cen2)<<" ";
if(cen1>cen2) swap(cen1,cen2);
}
memset(siz,0,sizeof(siz));
Dfs(cen1,0);
cout<<dis[cen1];
return 0;
}

浙公网安备 33010602011771号