CF793E Problem of offices
解题思路
初步思考
记叶子个数为 \(leaf\)。
假若 \(leaf\) 不是偶数直接就不可能了。
否则题目就等价于要求:\(a,b\) 以及 \(c,d\) 之间的叶子个数是 \(\frac{leaf}{2}-1\)。
刻画合法形态
考虑刻画最终合法方案的形态。
-
进入一棵子树之后,我们必定遍历完子树里所有的叶子之后才会去其它子树。
-
尝试确定 \(a,b,c,d\) 的顺序,这会简化我们考虑。
四个点没有本质区别,钦定 \(a\) 点在最前面。那么可行的顺序有以下两种:- \(a,c,b,d\)
- \(a.d.b.c\)
两种方案本质相同,不妨假设是 \(a,c,b,d\)。
四个对象太多了,限制条件有 \(a,b\) 以及 \(c,d\) 之间的。
考虑再简单一点的情况:我们只需要满足 \(a,b\) 之间叶子个数是 \(\frac{leaf}{2}-1\) 的限制。
根据上述“进入子树则选完”的发现,实际上我们就是要选择一些互不相交的可选子树,来凑成这个限制。
刻画选择方案
在刻画选择方案时,“\(a,b,c,d\) 在根的不同子树里”这个性质将会简化我们的考虑。
- 对于 \(a\) 到根的路径,其它分支出去的子树都可以被选;\(b\) 点亦然。
- 根的子树中,\(c\) 点所在的一定被选
- 除去 \(a,b,c,d\) 四个点所在的子树,根的其它子树都可以被选
这个问题显然可以直接做背包,用 \(\text{bitset}\) 优化 \(\text{DP}\) 做到 \(O(\frac{n^2}{w})\) 的时间复杂度解决。
背包问题的方案具有十分强的可调整性,因为被选子树的前后顺序可以任意调整。
容易发现,存在同时满足 \(a,b\) 以及 \(c,d\) 的限制的方案,等价于——存在满足 \(a,b\) 的方案,且存在满足 \(c,d\) 的方案。
把上述过程对 \(a,b\) 的过程对 \(c,d\) 再做一遍即可。
让我卡住的点
-
前面刻画合法形态想的不够清楚
-
后面刻画选择方案没想到可以将两个限制条件拆出来做
问题出在没有去尝试想更简单的情况
代码实现
点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
constexpr int N = 5010;
int n, leaf;
int fa[N], siz[N], anc[N];
vector<int> G[N];
bitset<N> s;
void Dfs(int u) {
if (G[u].empty()) {
++leaf;
siz[u] = 1;
}
for (int v: G[u]) {
Dfs(v);
siz[u] += siz[v];
}
}
void TagAnc(int u, int rt) {
anc[u] = rt;
for (int v: G[u]) {
TagAnc(v, rt);
}
}
bool Check2(int a, int c, int b) {
int lim = leaf / 2 - 1 - siz[anc[c]];
if (lim < 0) {
return 0;
}
auto f = s;
for (; fa[a] != 1; a = fa[a]) {
for (int v: G[fa[a]]) {
if (v != a) {
f |= f << siz[v];
}
}
}
for (; fa[b] != 1; b = fa[b]) {
for (int v: G[fa[b]]) {
if (v != b) {
f |= f << siz[v];
}
}
}
return f[lim];
}
bool Check(int a, int c, int b, int d) {
return Check2(a, c, b) && Check2(c, b, d);
}
int main() {
int a, b, c, d;
scanf("%d", &n);
scanf("%d %d %d %d", &a, &b, &c, &d);
FL(i, 2, n) {
scanf("%d", &fa[i]);
G[fa[i]].emplace_back(i);
}
Dfs(1);
if (leaf & 1) {
return puts("NO"), 0;
}
s[0] = 1;
for (int v: G[1]) {
TagAnc(v, v);
if (v != a && v != b && v != c && v != d) {
s |= s << siz[v];
}
}
if (Check(a, c, b, d) || Check(a, d, b, c)) {
puts("YES");
} else {
puts("NO");
}
return 0;
}

浙公网安备 33010602011771号