知识点:树的重心
定义:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。
性质:
性质 1 :树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
性质 2 :把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。
性质 3 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
题意:给出一颗树,求树的重心点以及重心点删除后中的最大子树。
分析:
根据性质 1 ,我们容易想到枚举每个点作为 root ,然后求一次其子树的点和,然后每次找出一个最大的子树,最后最小的子树既为重心带你,O(n^2)的时间,TEL
一个删除后得到的子树,要么是从子节点过来的,要么是从父节点过来的,子节点的子树节点和的我们可以通过一次简单的dfs维护求得son【child】。
而父亲点过来的为总的节点数 N - son【father】 - 1
那么事情就变的简单了,我们只要维护dp【father】 = max(son【child】,N - son【father】-1)
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
using namespace std;
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 20100;
vector<int> v[N];
int vis[N],dp[N],son[N];
int ans,asize;
int n;
void DFS(int s)
{
vis[s] = 1;
son[s] = 0;
int blance = 0;
int size = v[s].size();
for (int j = 0;j < size;j++)
{
int u = v[s][j];
if (vis[u]) continue;
DFS(u);
son[s] += son[u]+1;
blance = max(blance,son[u]+1);
}
blance = max(blance,n - son[s] - 1);
if (blance < asize || blance == asize && s < ans)
ans = s,asize = blance;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
ans=1;
Del(vis,0);Del(son,0);
Del(dp,0);
asize=0x3f3f3f3f;
DFS(1);
printf("%d %d\n",ans,asize);
for(int i=0;i<=n;i++)
v[i].clear();
}
return 0;
}