Loading

Codeforces Round #670 (Div. 2) C. Link Cut Centroids(树的重心)

题目大意是给定一棵树,删除一条边再添加一条边,使这棵树重心唯一。

对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心。重心有如下性质:

以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。

树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。

把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。

在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。

至多有两个重心。如果树有两个重心,那么它们相邻。此时树一定有偶数个节点,且可以被划分为两个大小相等的分支,每个分支各自包含一个重心。

证明可见https://zhuanlan.zhihu.com/p/357938161

那么对于这个题,如果树只有一个重心,那么随便找一条边添加再删除即可;如果有两个重心,则选一个重心的某棵子树移到另一个重心即可,可以证明移走子树的重心不再是重心,且另一个重心仍然为重心。

#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n, head[N], ver[2 * N], Next[2 * N], tot = 0;
void add(int x, int y) {
	ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
vector<int> c;
int mx[N], sz[N];
int mn = 0x3f3f3f3f;
void dfs(int x, int pre) {
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == pre) continue;
		dfs(y, x);
		sz[x] += (sz[y] + 1);
		mx[x] = max(mx[x], sz[y] + 1);
	}
	mx[x] = max(mx[x], n - 1 - sz[x]);
	mn= min(mn, mx[x]);
}
void solve() {
	cin >> n;
	tot = 0;
	mn = 0x3f3f3f3f;
	c.clear();
	for(int i = 0; i <= n; i++) {
		head[i] = 0;
		mx[i] = 0;
		sz[i] = 0;
	}
	for(int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs(1, 0);
	for(int i = 1; i <= n; i++) {
		if(mx[i] == mn) {
			c.push_back(i);
		}
	}
	if(c.size() == 1) {
		for(int i = head[c[0]]; i; i = Next[i]) {
			int y = ver[i];
			cout << c[0] << " " << y << endl;
			cout << c[0] << " " << y << endl;
			return;
		}
	} else {
		int tmp;
		for(int i = head[c[0]]; i; i = Next[i]) {
			int y = ver[i];
			if(y == c[1]) continue;
			cout << c[0] << " " << y << endl;
			tmp = y;
			break;
		}
		cout << c[1] << " " << tmp << endl;
		return;
	}
}
int main() {
	int T;
	cin >> T;
	while(T--) {
		solve();
	}
	return 0;
}
posted @ 2022-11-16 11:30  脂环  阅读(34)  评论(0编辑  收藏  举报