题解:P14637 [NOIP2025] 树的价值 / tree(民间数据)
约定:
- 对于一个节点
x ,如果x 取值对x 的{\rm mex} 有贡献,我们称x 是一个红点。 - 对于一个节点
x ,如果x 取值对x 的{\rm mex} 没有贡献,我们称x 是一个蓝点。
如上图所示:
-
红点
x 的贡献 =x 到根路径上连续红点段长度 + 红点段之后连续蓝点段长度。但是,蓝点段贡献只能被一段红点所享有,例如中间图,点
7 和点8 的路径上都有蓝点3 ,此时点3 的贡献只能被一段享有,因此7 的贡献为2 ,8 的贡献为1 。由上面这一点,我们可以规定一个点最多只有一个红点儿子,因为多个红点儿子,必然由红点儿子贡献为
1 ,将其变成蓝点不会变差。 -
蓝点
x 的贡献 =x 祖先第一个红点的贡献例如
5 的贡献为2 ,因为5 的祖先中第一个红点为2 ,其贡献为2 。
设
对于红点,每个点只有一个红点儿子,其他都是蓝点儿子,而根据蓝点贡献计算方式可知这些蓝点儿子的贡献与
对于蓝点
如果
如上图所示,这个图有很多性质:
-
-
-
-
如果 $v$ 的贡献大于 $k+1$,则可以将 $v$ 的父亲变成红点,其新的贡献为 $v$ 的贡献减去 $1$,大于 $k$,更优。因此 $v$ 的贡献不超过 $k+1$。 如果 $v$ 的贡献小于 $k+1$,则将 $v$ 变成蓝点贡献不会变差。 综上:$v$ 贡献为 $k+1$。实际上根据上面的分析 $v$ 的贡献为 $k$ 也是优的,但是存在例外情况,例如第一张图点 $7$ 的贡献只能为 $2$,不能为 $1$。 -
根据红点贡献计算方式,
v 的贡献为{\rm dep}(v)-{\rm dep}(x)+1 ,因此k={\rm dep}(v)-{\rm dep}(x) 。
综上,转移方程为
我们要维护后面的那个求和,可以使用数据结构,例如 DP 到
最后答案为
#include <bits/stdc++.h>
const int N = 8000 + 7;
const int M = 800 + 7;
std::vector<int> e[N];
int bit[M][N], f[N][M], g[N][M], n, m;
int dfn[N], nfd[N], ed[N], cdfn, dep[N];
void add(int *bit, int x, int v) {
while(x <= n) {
bit[x] += v;
x += x & -x;
}
}
int ask(int *bit, int x) {
int ret = 0;
while(x) {
ret += bit[x];
x -= x & -x;
}
return ret;
}
void dfs(int x) {
dfn[x] = ++cdfn;
nfd[cdfn] = x;
for(int v: e[x]) {
dep[v] = dep[x] + 1;
dfs(v);
}
ed[x] = cdfn;
for(int i = 1; i <= dep[x]; ++i) {
f[x][i] = g[x][i] = i;
for(int v: e[x])
g[x][i] += g[v][i];
for(int v: e[x])
f[x][i] = std::max(f[x][i], f[v][i + 1] + g[x][i] - g[v][i]);
for(int v: e[x]) {
add(bit[i], dfn[v], g[x][i] - g[v][i]);
add(bit[i], ed[v] + 1, g[v][i] - g[x][i]);
}
}
for(int j = dfn[x] + 1; j <= ed[x]; ++j) {
int i = dep[nfd[j]] - dep[x] + 1;
g[x][i - 1] = std::max(g[x][i - 1], ask(bit[i - 1], j) + f[nfd[j]][i]);
}
}
void solve() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
e[i].clear();
for(int i = 2, x; i <= n; ++i) {
scanf("%d", &x);
e[x].push_back(i);
}
memset(bit, 0, sizeof bit);
cdfn = 0;
dep[1] = 1;
dfs(1);
printf("%d\n", f[1][1]);
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
int cases;
scanf("%d", &cases);
while(cases--)
solve();
return 0;
}