树的重心
定义:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。
性质:
性质 1 :树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
性质 2 :把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。
性质 3 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
例题: POJ 1655
题目分析:
求树的重心的方法,我们把每棵树的所连接的最大子树求出来, 然后所有节点的最大子树中最小的那个就是树的重心。
关于求所有树的最大子树求法,我们可以求出每个点下方节点的数量,然后再求每个子树的时候把他的节点数量给记录一下。最后再反向求一下父亲节点的子树的数量就行了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<stack> #include<map> using namespace std; typedef long long LL; const LL INF = 1e9+7; const LL MOD = 1e9+7; const LL MAXN = 20005; vector<vector<int> > G; int dp[MAXN], num[MAXN], n;///该点所保存的最大子树,num代表该点的子节点的数量 void DFS(int root) { num[root] = 1; int len = G[root].size(); for(int i=0; i<len; i++) { int v = G[root][i]; if(num[v] == 0) { DFS(v); dp[root] = max(dp[root], num[v]); num[root] += num[v];///下方节点的总个数 } } dp[root] = max(dp[root], n-num[root]); } int main() { int T, a, b; scanf("%d", &T); while(T --) { scanf("%d", &n); G.clear(); G.resize(n + 3); memset(dp, -1, sizeof(dp)); memset(num, 0, sizeof(num)); for(int i=1; i<n; i++) { scanf("%d %d", &a, &b); G[a].push_back(b); G[b].push_back(a); } DFS(1); int ans = 1; for(int i=2; i<=n; i++) { if(dp[ans] > dp[i]) ans = i; } printf("%d %d\n",ans, dp[ans]); } return 0; }