求树的重心

树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。

我们假设以 1 为根节点,那么我们只能求到任意一个点的子树的大小,并不知道这个点的父节点方向的联通分量的节点个数,但我们已知这个节点的子树大小,并且所有节点数目为 n ,那么我们可以知道这个点的父节点方向的联通分量的节点个数,然后每个点的子树的最大节点数就算出来了,然后遍历一次,就得到了树的重心。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define ll long long
using namespace std;
const int MAXN = 1e5 + 5;
struct edge
{
	int to;
	int nex;
}e[MAXN * 2];
int head[MAXN], tot = 1;
void add(int a, int b)
{
	e[tot] = edge{ b,head[a] };
	head[a] = tot++;
}
int num[MAXN], dp[MAXN], n;
void init()
{
	memset(head, -1, sizeof(head));
	tot = 1;
	memset(num, 0, sizeof(num));
	memset(dp, 0, sizeof(dp));
}
void dfs(int u, int fa)
{
	num[u] = 1;
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int v = e[i].to;
		if (v != fa)
		{
			dfs(v, u);
			dp[u] = max(dp[u], num[v]);
			num[u] += num[v];
		}
	}
	dp[u] = max(dp[u], n - num[u]);
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		init();
		int a, b;
		scanf("%d", &n);
		for (int i = 1; i < n; i++)
		{
			scanf("%d%d", &a, &b);
			add(a, b);
			add(b, a);
		}
		dfs(1, 0);
		int res = n, end = 0;
		for (int i = 1; i <= n; i++)
		{
			if (dp[i] < res)
			{
				res = dp[i];
				end = i;
			}
		}
		printf("%d %d\n", end, res);
	}
}