题意
来自洛谷:
思路
记录每个点 \(u\) 所在子树可以删去的最大的部分 \(part1\) 和次大的部分 \(part2\) 和除了 \(u\) 的子树以外的部分可以删去的最大的部分 \(up\),这些部分必须要求小于等于 \(\dfrac{n}{2}\),和找树的中心(注意不是重心)的思路差不多。
注意:\(part1, part2\) 不能同时更新,\(up\) 的更新要注意点的取舍。
然后记录一下结点 \(u\) 子树最大的结点 \(maxch[u]\)。
对于每个点,如果他本来就是重心那它不用变。
否则其子树或者子树以外的部分必有大于 \(\dfrac{n}{2}\) 的部分。
子树大于 \(\dfrac{n}{2}\),如果 \(sz[maxch[u]] - part1[maxch[u]] \le \dfrac{n}{2}\),说明可以割掉这一部分再接到 \(u\) 上去,这样任何一个部分也不会超过 \(\dfrac{n}{2}\)。
对于子树之外的一部分超过 \(\dfrac{n}{2}\),那就看如果 \((n - sz[u] - up[u]) \le \dfrac{n}{2}\),那说明满足条件,和上面类似。
代码
细节挺多,本来想着口胡一下就过了算了。
// Problem: Centroids
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF708C
// Memory Limit: 500 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// up, part1, part2;
#include <bits/stdc++.h>
using namespace std;
const int N = 400010, M = 800010;
struct edge {
int to, next;
} e[M];
int head[N], idx = 1;
void add(int u, int v) {
idx++, e[idx].to = v, e[idx].next = head[u], head[u] = idx;
}
int n;
int sz[N];
int maxch[N], maxchr[N];
int part1[N], part2[N], up[N]; // part1: 最大的小于等于 n / 2 的子树,part2: 第二大的小于等于 n / 2 的子树
int chver[N]; // 记录 part1 对应的结点
int f[N];
bool res[N];
bool updatepart(int u, int x, int c) { // 更新 part1, part2
if (x * 2 > n) return false;
bool flag = false;
if (part1[u] < x) {
part2[u] = part1[u];
part1[u] = x;
chver[u] = c;
flag = true;
}
else if (part2[u] < x) {
part2[u] = x;
flag = true;
}
return flag;
}
void dfs(int u, int fa) { // dfs 记录子树大小,并找到 part1, part2 及 chver
sz[u] = 1; f[u] = fa;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs(to, u);
sz[u] += sz[to];
if (sz[to] > maxch[u]) { // 记录 u 的最大子树
maxch[u] = sz[to];
maxchr[u] = to;
}
if (!updatepart(u, sz[to], to)) updatepart(u, part1[to], chver[to]);// 两者不能同时更新
}
}
void dfsup(int u, int fa) {
for (int i = head[u]; i; i = e[i].next) { // 记录每个点除了这个子树的部分的 part1
int to = e[i].to;
if (to == fa) continue;
up[to] = up[u];
if ((n - sz[u]) * 2 <= n) up[to] = max(up[to], n - sz[u]);
if (chver[u] == chver[to] || chver[u] == to) up[to] = max(up[to], part2[u]);
else up[to] = max(up[to], part1[u]);
dfsup(to, u);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
dfs(1, 0);
dfsup(1, 0);
for (int u = 1; u <= n; u++) {
int mxs = max(maxch[u], n - sz[u]);
if (mxs * 2 <= n) {
res[u] = true;
continue;
}
if (maxch[u] * 2 > n) { // u 的子树的最大部分大于 n / 2
if ((maxch[u] - part1[maxchr[u]]) * 2 <= n) { // u 的子树的最大的部分减去能减去的最大部分
res[u] = true;
}
}
else {
if ((n - sz[u] - up[u]) * 2 <= n) { // u 的子树之外最大部分大于 n / 2
res[u] = true; // u 的子树之外的部分减去能减去的最大部分
}
}
}
for (int i = 1; i <= n; i++) cout << res[i] << ' ';
return 0;
}
标签:sz,CF708C,int,Centroids,part1,part2,子树,maxch
From: https://www.cnblogs.com/Yuan-Jiawei/p/18348502