Description
给定一棵树,求不互为祖先的点对的最大个数。
Solution
考虑树形 DP。
设 \(dp_u\) 表示根节点为 \(u\) 的子树的答案。
分类讨论:
设根节点 \(u\) 的重儿子为 \(v\)。
-
当 \(size_v \le \lfloor \frac{size_u - 1}{2}\rfloor\) 时。考虑一种构造使得答案最大:可以对这棵树计算 dfs 序,由同一棵子树内的 dfs 序连续,则可以构成若干段长度均不大于 \(\lfloor \frac{size_u - 1}{2}\rfloor\) 的序列。对于每一段序列里的任意节点,它的 dfs 序加上 \(\lfloor \frac{size_u - 1}{2}\rfloor\) 后的点一定与它不属于同一个序列。所以最大值为 \(\lfloor \frac{size_u - 1}{2}\rfloor\)。
-
当 \(size_v > \lfloor \frac{size_u - 1}{2}\rfloor\) 时:让 \(v\) 以外的节点都和 \(v\) 内未配对的节点两两配对。答案即为 \(f_v + size_u - size_v - 1\)。但是, \(v\) 中的未配对的点不够与 \(v\) 外的点进行配对,这时需要将 \(v\) 中已配对的点拆开与外面的点进行配对。但是,这样可能算重,所以和上界 $$\lfloor \frac{size_u - 1}{2}\rfloor$$ 比较,取小者。
故得转移方程:
\[f_u=\left\{\begin{aligned} &\lfloor \frac{size_u - 1}{2}\rfloor & \quad \rm{if} \quad size_v \le \lfloor \frac{size_u - 1}{2}\rfloor \\ &\min(f_v + size_u - size_v - 1, \lfloor \frac{size_u - 1}{2}\rfloor) & \quad \rm{if} \quad size_v > \lfloor \frac{size_u - 1}{2}\rfloor\end{aligned}\right.
\]
Code
#include <bits/stdc++.h>
using namespace std;int t, n, siz[(int)2e5 + 5], f[(int)2e5 + 5];
vector<int> vt[(int)2e5 + 5];void dfs(int u, int fa) {siz[u] = 1;int z = 0;for (int v : vt[u]) {if (v == fa)continue;dfs(v, u);siz[u] += siz[v];if (siz[v] > siz[z]) z = v;}if (siz[z] > (siz[u]-1) / 2) f[u] = min(f[z] + siz[u] - siz[z] - 1, (siz[u] - 1) / 2);else f[u] = (siz[u] - 1) / 2;
}int main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> t;while (t--) {cin >> n;memset(f, 0, sizeof(f));memset(siz, 0, sizeof(siz));for (int i = 1; i <= n; ++i)vt[i].clear();for (int i = 2; i <= n; ++i) {int p;cin >> p;vt[p].push_back(i);}dfs(1, 0);cout << f[1] << '\n';}return 0;
}
感谢 @zhengjingtian 的贡献!