树的重心(性质+模版)
定义:
在一棵树上找到一个点,把这个点作为树的根的有根树,最大子树的节点数最小。(就是把该点去掉以后,最大连通块的节点数最小)
性质:
1、一棵树的重心至多有两个,且相邻。
2、树中某个点到所有点到距离之和中,到重心的距离之和是最小的。如果有两个重心,那么到它们的距离之和一样。
3、把两棵树用一条边相连,新树的重心在原来两棵树的重心的连线上。
4、一棵树添加或删除一个结点,重心最多移动一条边的位置。
求法:
通过DFS求出某个点向下的子树的节点数的大小,在用总节点数减去当前子树的节点数得到向上的子树的节点数的大小,再通过定义去求重心。
代码:
模版题:Poj 1655
1 //#include<bits/stdc++.h> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<string> 7 #include<vector> 8 #include<stack> 9 #include<bitset> 10 #include<cstdlib> 11 #include<cmath> 12 #include<set> 13 #include<list> 14 #include<deque> 15 #include<map> 16 #include<queue> 17 #define ll long long 18 #define MOD 1000000007 19 #define INF 0x3f3f3f3f 20 #define mem(a,x) memset(a,x,sizeof(a)) 21 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 22 using namespace std; 23 const int maxn = 20005 ; 24 vector<int>tree[maxn]; 25 int minNode,minBalance; 26 //minNode 表示当前的重心 27 //minBalance 表示当前重心下的最大子树结点个数 28 int d[maxn]; 29 //d[i]表示以 i 为根的子树结点数 30 int n; 31 void getCentroid(int u,int fa) 32 { 33 d[u]=1; 34 int maxsub=0; 35 for(int i=0;i<tree[u].size();i++){ 36 int v=tree[u][i]; 37 if(v==fa)continue; 38 getCentroid(v,u); 39 d[u]+=d[v]; 40 maxsub=max(d[v],maxsub); 41 } 42 maxsub=max(maxsub,n-d[u]); 43 if(maxsub<minBalance){ 44 minNode=u; 45 minBalance=maxsub; 46 } 47 } 48 int main() 49 { 50 int T; 51 cin>>T; 52 while(T--){ 53 cin>>n; 54 for(int i=1;i<=n;i++){ 55 tree[i].clear(); 56 } 57 for(int i=1;i<n;i++){ 58 int u,v; 59 scanf("%d %d",&u,&v); 60 tree[u].push_back(v); 61 tree[v].push_back(u); 62 } 63 minNode=0; 64 minBalance=INF; 65 getCentroid(1, 0); 66 printf("%d %d\n",minNode,minBalance); 67 } 68 return 0; 69 }
越自律,越自由