定义

树的重心是指树上的某个节点,满足删除当前点之后,生成的树的大小的最大值最小。

性质

性质1

以树的重心为根,那么根节点的每棵子树的大小都小于等于\(\frac{n}{2}\)

性质2

每棵子树的大小都小于等于\(\frac{n}{2}\)的点一定是这棵树的重心。即性质\(1\)的逆命题。

性质3

树中所有点到某个点的距离和中,到重心的距离和最小。

性质4

两棵树通过一条新边连接成一棵新树。新树的重心一定再原来两棵树的重心之间的简单路径上。

性质5

一棵树最多有两个重心

求树的重心

求树的重心有两种方法。

方法1

dfs一遍,处理出每个节点\(u\)的最大子树。对于父亲节点方向的子树。可以用\(n-siz[u]\)得到。

方法2

“调整法”。先假设当前点\(u\)为最优点。如果孩子\(v\)不满足\(n-siz[v] \geq siz[v]\),那么树的重心一定再子树\(v\)中。否则u为树的重心。

代码(方法1)

//2019-01-06 19:14:32
///Author: wxyww
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 20000 + 100;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
struct node {
    int v,nxt;
}e[N * 2];
int head[N],ejs;
void add(int u,int v){ 
    e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
}
int siz[N],n,ans[N];
void dfs(int u,int father) {
    siz[u] = 1;
    for(int i = head[u];i;i = e[i].nxt) {
        int v = e[i].v;
        if(v == father) continue;
        dfs(v,u);
        siz[u] += siz[v];
        ans[u] = max(ans[u],siz[v]);
    }
    ans[u] = max(ans[u],n - siz[u]);
}
int main() {
    int T = read();
    while(T--) {
        ejs = 0;
        memset(ans,0,sizeof(ans));
        memset(siz,0,sizeof(siz));
        memset(head,0,sizeof(head));
        n = read();
        for(int i = 1;i < n;++i) {
            int u = read(),v = read();
            add(u,v);add(v,u);
        }
        dfs(1,0);
        int anss = 0;
        ans[0] = N;
        for(int i = 1;i <= n;++i) {
            if(ans[i] < ans[anss]) anss = i;
        }
        printf("%d %d\n",anss,ans[anss]);
    }

    return 0;
}