2019ICPC徐州区域赛 Kill the tree

https://nanti.jisuanke.com/t/42552

以上是交题网站计蒜客

本题要求找到所有子树的所有重心

性质1 : 两棵树合并,新树的重心在两旧树重心连线上

性质2  :子树的重心一定在重儿子上

性质3  :树上所有点到重心的距离的和最小

 

当siz[root] - siz[x] > siz[x] 时,x要往上爬  (某大佬的推理)

这种写法是每次都得都往上爬

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;
const int maxn = 2e5 + 777;
struct Node {
	int to;
	int next;
}G[maxn * 2];
int z = 0;
int head[maxn];

int add(int x, int y) {
	G[++z].to = y;
	G[z].next = head[x];
	head[x] = z;
	return 0;
}
int n;
int dp[maxn];
int fa[maxn], siz[maxn];
int dep[maxn];
vector<int>ans[maxn];

int unin(int root, int x, int y) {
	while (dep[x] > dep[root] && siz[root] - siz[x] > siz[x]) {//让x往上爬
		x = fa[x];
	}
	while (dep[y] > dep[root] && siz[root] - siz[y] > siz[y]) {
		y = fa[y];
	}
	if (dep[x] > dep[y]) dp[root] = x;
	else dp[root] = y;
	return 0;
}
int son[maxn];

int dfs(int x, int f,int d) {
	int s = 0;
	siz[x] = 1;
	dep[x] = d;
	fa[x] = f;

	for (int i = head[x]; i; i = G[i].next) {
		int p = G[i].to;
		if (p == f) continue;
		dfs(p, x, d + 1);
		siz[x] += siz[p];		//先把x和p合并起来
		unin(x, dp[x], dp[p]);  //然后开始往上爬
	}
	return 0;
}



int main() {
	int be, en;
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &be, &en);
		add(be, en);
		add(en, be);
	}
	for (int i = 1; i <= n; i++) dp[i] = i;
	dfs(1, 0, 1);
	for (int i = 1; i <= n; i++) {
		int x = dp[i];
		ans[i].push_back(x);
		int t = fa[x];
		if (t != 0 && siz[i] - siz[x] == siz[x]) {
			ans[i].push_back(t);
		}
		sort(ans[i].begin(), ans[i].end());
		
		if (ans[i].size() == 1) {
			printf("%d\n", ans[i][0]);
		}
		else {
			printf("%d %d\n", ans[i][0], ans[i][1]);
		}
	}

	
	return 0;
}

这个是找到重儿子,从重儿向上爬,好理解一些,

这个更快一些

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;
const int maxn = 2e5 + 777;
struct Node {
	int to;
	int next;
}G[maxn * 2];
int z = 0;
int head[maxn];

int add(int x, int y) {
	G[++z].to = y;
	G[z].next = head[x];
	head[x] = z;
	return 0;
}
int n;
int dp[maxn];
int fa[maxn], siz[maxn];
int dep[maxn];
vector<int>ans[maxn];

int unin(int root, int x, int y) {
	while (dep[x] > dep[root] && siz[root] - siz[x] > siz[x]) {//让x往上爬
		x = fa[x];
	}
	while (dep[y] > dep[root] && siz[root] - siz[y] > siz[y]) {
		y = fa[y];
	}
	if (dep[x] > dep[y]) dp[root] = x;
	else dp[root] = y;
	return 0;
}
int son[maxn];

int dfs(int x, int f,int d) {
	int s = 0;
	siz[x] = 1;
	dep[x] = d;
	fa[x] = f;

	for (int i = head[x]; i; i = G[i].next) {
		int p = G[i].to;
		if (p == f) continue;
		dfs(p, x, d + 1);
		siz[x] += siz[p];		//先把x和p合并起来
		if (s < siz[p]) {
			s = siz[p];
			son[x] = p;
		}
	}
	dp[x] = x;
	if (!son[x]) return 0;
	int p = dp[son[x]];//重儿子的重心
	while (dep[p] > dep[x] && siz[x] - siz[p] > siz[p]) {//满足条件就向上怕,最多能到根
		p = fa[p];
	}
	dp[x] = p;
	return 0;
}


int main() {
	int be, en;
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &be, &en);
		add(be, en);
		add(en, be);
	}
	dfs(1, 0, 1);
	for (int i = 1; i <= n; i++) {
		int x = dp[i];
		ans[i].push_back(x);
		int t = fa[x];
		if (t != 0 && siz[i] - siz[x] == siz[x]) {
			ans[i].push_back(t);
		}
		sort(ans[i].begin(), ans[i].end());
		
		if (ans[i].size() == 1) {
			printf("%d\n", ans[i][0]);
		}
		else {
			printf("%d %d\n", ans[i][0], ans[i][1]);
		}
	}

	
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/lesning/p/12560509.html