题解:P17056 [NWERC 2022] 高质量树 / High-quality Tree
Linnanyu123 · · 题解
我们需要找到最小的叶子移除数量,使得给定有根二叉树的所有子树都满足平衡条件。一个子树是平衡的,当且仅当其左右子树的深度差不超过
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i < (b); ++i)
using VI = vector<int>;
const int NN = 2e5 + 4;
int Sz[NN], F[NN]; // Sz[u]:子树u大小; F[u]:可构成的强平衡树的最大深度
VI G[NN];
int dfs(int u = 1, int fa = 0) { // 计算Sz和F
Sz[u] = F[u] = 1; // 初始化:自己算一个节点,深度至少为1
int f0 = 0, f1 = 0; // 记录两个子节点的F值
for (int v : G[u])
if (v != fa) // 递归处理子节点, 累加子树大小
Sz[u] += dfs(v, u), (f0 ? f1 : f0) = F[v]; // 将子节点的F值存入f0和f1
F[u] = min(f0, f1) + (f0 != f1) + 1;
return Sz[u]; // 子树深度相同,新深度为f0+1, 否则为min(f0,f1)+2
}
// 计算最少移除节点数,d: 父节点给予的深度限制
int dfs1(int u, int fa, int d) {
if (d == 0) return Sz[u]; // 如果深度限制为0,必须移除整个子树
int r = 0; // 记录需要移除的节点数
for (int v : G[u]) // 递归计算每个子树需要移除的节点数
if (v != fa) // v的深度限制是min(d-1, F[v])
r += dfs1(v, u, min(d - 1, F[v]));
return r;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int n;
cin >> n;
for (int i = 1, u, v; i < n and cin >> u >> v; i++)
G[u].push_back(v), G[v].push_back(u);
dfs(), printf("%d\n", dfs1(1, 0, F[1]));
return 0;
}