考虑这个不存在 \(\texttt{YY}\) 的限制,与 \(\texttt{XX}\) 个数为变量的限制相比较,看起来 \(\texttt{Y}\) 就更特殊,于是考虑从 \(\texttt{Y}\) 的视角来分析问题。
同时考虑到因为有 \(A + B + C = n - 1\),所以 \(\texttt{XX}\) 其实也不是很重要,因为只需要让 \(\texttt{XY}\) 和 \(\texttt{YX}\) 的个数满足条件就可以了。
注:这之后的分析默认遵守无 \(\texttt{YY}\),即不存在自己与父亲皆为 \(\texttt{Y}\) 的情况。
于是可以考虑先让整颗树都是 \(\texttt{X}\),把一个 \(\texttt{X}\) 改变成 \(\texttt{Y}\) 的影响。
手玩一下可以知道若为 \(\texttt{Y}\) 的节点的集合为 \(S\),则有 \(B = |S| - [1\in S], C = \sum\limits_{u\in S} |\operatorname{son}_u|\)。
再考虑到这题的特殊条件,\(|\operatorname{son}_u| = 2 \operatorname{or} 0\)。
那么就有 \(C = 2|S\cap \complement_{1, \cdots, n} \operatorname{leaf}|\),即 \(S\) 中非叶子结点的个数的 \(2\) 倍。
那么能发现 \(\frac{C}{2}\) 就是 \(S\) 中非叶子结点的个数,又因为 \(B = |S| - [1\in S]\),那么就可以知道 \(S\) 中的叶子节点个数为 \(B - \frac{C}{2} + [1\in S]\),对于这个 \([1\in S]\) 因为实际上就涉及 \(2\) 种情况是好处理的。
于是接下来的重点是是否存在 \(x\) 个非叶子节点与 \(y\) 个叶子节点的选取。
那么一个想法是用一个树形背包来合并信息,记 \(f_{u, y, x, 0 / 1}\) 为 \(u\) 节点子树内能否存在选了 \(y\) 个叶子节点 \(x\) 个非叶子节点且自身是否选了的方案,但显然是过不了的。
考虑到因为有 \(2\) 个限制,考虑固定下一边的限制。
例如考虑固定下叶子节点的个数 \(y\),判定是否存在 \(x\) 个非叶子结点的个数。
能够发现的是 \(x\) 是具有单调性的,也就是对于同样的子树与同样的 \(y\) 及同样该节点是否选取的情况,合法的 \(x\) 应当是一个前缀的。
即对于同样的 \(u, x, p\),应存在一个 \(\max x\),满足 \(f_{u, y, x, p} = [x\le \max x]\)。
那么就可以记 \(f_{u, y, p} = \max x\),用树形背包合并即可。
最后记得要讨论一下 \([1\in S]\)。
时间复杂度 \(\mathcal{O}(n^2)\)。
#include<bits/stdc++.h>
constexpr int inf = 1e9;
const int maxn = 1e4 + 10;
int fa[maxn], siz[maxn];
int f[maxn][maxn][2];
int ls[maxn], rs[maxn];
inline void solve() {
int n, a, b, c; scanf("%d%d%d%d", &n, &a, &b, &c);
for (int i = 1; i <= n; i++) ls[i] = rs[i] = 0;
for (int i = 2; i <= n; i++) scanf("%d", &fa[i]), (ls[fa[i]] ? rs[fa[i]] : ls[fa[i]]) = i;
if (c & 1)
return puts("No"), void();
for (int u = n; u; u--) {
if (! ls[u]) {
siz[u] = 1;
f[u][0][0] = f[u][0][1] = 0;
f[u][1][1] = 0, f[u][1][0] = -inf;
continue;
}
siz[u] = u > 1 ? siz[ls[u]] + siz[rs[u]] : n;
for (int i = 0; i <= siz[u]; i++)
for (int j : {0, 1})
f[u][i][j] = -1e9;
for (int x : {0, 1})
for (int i = 0; i <= siz[ls[u]]; i++)
for (int j = 0; j <= siz[rs[u]]; j++)
f[u][i + j][x] = std::max(f[u][i + j][x], f[ls[u]][i][x ^ 1] + f[rs[u]][j][x ^ 1] + x);
if (u > 1)
for (int i = 0; i <= siz[u]; i++)
f[u][i][1] = std::max(f[u][i][1], f[u][i][0]);
}
puts((b >= c / 2 - 1 && f[1][b - c / 2 + 1][1] >= c / 2)
|| (b >= c / 2 && f[1][b - c / 2][0] >= c / 2) ? "Yes" : "No");
}
int main() {
int T; scanf("%d", &T);
while (T--) solve();
return 0;
}
标签:Atcoder,XXYX,int,个数,texttt,Tree,叶子,maxn,节点
From: https://www.cnblogs.com/rizynvu/p/18444861