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; }
寻找真正的热爱