C.Link Cut Centroids(树的重心)
树的重心
一:定义
存在树中的一个结点,当去掉该结点及其所连的边时,会分成多个连通分量,当该节点为重心时,最大连通分量的结点数最少。
如下图: 1,2 均为树的重心,删掉1后,最大连通分量节点数为 3(结点2,5,6)
[外链图片转存中…(img-Z9xN8ITj-1599994169819)]
二:性质
1:删除重心后所得的所有子树,节点数不超过原树的1/2;
2:一棵树最多有两个重心,且相邻;
3:树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;(有权树)
4:树删除或添加一个叶子节点,重心最多只移动一条边
三:寻找树的重心
回溯法:dfs
int son[maxn]; //该点的子节点总数
int balance = n/2;
int root[maxn], cnt; //存重心
void dfs(int x,int pre){
son[x] = 1;
int res = 0;
for(int i=0;i<g[x].size();i++){
int v = g[x][i];
if(v != pre){
dfs(v,x);
son[x] += son[v];
res = max(res, son[v]); //最大子树的节点数
}
}
res = max(res, n-son[x]); //与父节点的比较,以x=2为例,比较父节点1,3,4组成的子树
if(res < balance){
cnt = 0;
root[cnt++] = x;
balance = res;
}
else if(res == balance){
root[cnt++] = x;
}
}
四,经典例题
codeforces #670 div2 C题
https://codeforces.com/contest/1406/problem/C
题解代码:
#include <bits/stdc++.h> //无边权的树的重心
#define maxn 110000
using namespace std;
int t,n;
vector<int> g[maxn+2];
int balance,cnt,root[maxn+2],son[maxn+2],tag[maxn+2];
void init(){
for(int i=0;i<n+10;i++){
g[i].clear();
root[i] = son[i] = tag[i] = 0;
}
balance = n/2;
cnt = 0;
}
void dfs(int x,int pre){
son[x] = 1;
int res = 0;
for(int i=0;i<g[x].size();i++){
int v = g[x][i];
if(v!=pre){
dfs(v,x);
son[x] += son[v];
res = max(res, son[v]); //最大子树的节点数
}
}
res = max(res, n-son[x]); //当x为非根的父节点时,要比较它子节点最大值和父 节 点 所连结点总数
if(res < balance){
cnt = 0;
root[cnt++] = x;
balance = res;
}
else if(res == balance)
root[cnt++] = x;
}
void dfs2(int x,int pre){
for(int i=0;i<g[x].size();i++){
int v = g[x][i];
if(v != pre){
tag[v] = x;
dfs2(v, x);
}
}
}
int main(){
cin>>t;
while(t--){
cin>>n;
init();
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,0);
if(cnt==1){
int v1 = 1;
int t1 = g[1][0];
cout<<v1<<" "<<t1<<endl;
cout<<v1<<" "<<t1<<endl;
}
else if(cnt==2){
int x1 = root[0];
int x2 = root[1];
int x3;
dfs2(x1,-1);
for(int i=0;i<g[x1].size();i++){
int v = g[x1][i];
if(v!=x2){
x3 = v;break;
}
}
cout<<x1<<" "<<x3<<endl;
cout<<x2<<" "<<x3<<endl;
}
}
return 0;
}