CF793E Problem of offices

题目链接

解题思路

初步思考

记叶子个数为 \(leaf\)
假若 \(leaf\) 不是偶数直接就不可能了。
否则题目就等价于要求:\(a,b\) 以及 \(c,d\) 之间的叶子个数是 \(\frac{leaf}{2}-1\)

刻画合法形态

考虑刻画最终合法方案的形态。

  • 进入一棵子树之后,我们必定遍历完子树里所有的叶子之后才会去其它子树。

  • 尝试确定 \(a,b,c,d\) 的顺序,这会简化我们考虑。
    四个点没有本质区别,钦定 \(a\) 点在最前面。那么可行的顺序有以下两种:

    1. \(a,c,b,d\)
    2. \(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;
}
posted @ 2025-04-09 11:02  徐子洋  阅读(13)  评论(0)    收藏  举报