树的重心

定义

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

性质

性质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
",anss,ans[anss]);
	}

	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/10332920.html