ARC101E Ribbons on Tree 题解

作者: xht37 分类: 题解 发布时间: 2020-04-24 17:51

Visits: 300

ARC101E Ribbons on Tree

题意

  • 给定一棵 $n$ 个点的树,保证 $n$ 为偶数。
  • 将 $n$ 个点分成 $\frac n2$ 个无序点对。
  • 对于每个点对,将树上两点之间的最短路径上的边染色。
  • 问有多少种方案使得每条边都被染色。
  • $n \le 5 \times 10^3$,答案对 $10^9+7$ 取模。

题解

考虑容斥,设 $f(S)$ 表示强制不覆盖边集 $S$ 中的边的方案数,则答案为 $\sum_{S \subseteq E} (-1)^{|S|} f(S)$。

考虑 $f(S)$ 如何计算,设 $S$ 中的边割掉后将树分成了大小为 $a_{1},a_2,\cdots,a_t$ 这 $t$ 个连通块,则 $f(S) = \prod_{t=1}^t \left[2 | a_i\right]\left(a_i-1\right)!!$,其中 $x!!$ 表示 $x(x-2)(x-4)\cdots$。

考虑树形 DP,设 $f_{i,j}$ 表示考虑 $i$ 的子树,割掉若干条边后 $i$ 所在的连通块大小为 $j$,不包含 $i$ 所在的连通块的贡献总和。

转移就是树上背包,时间复杂度 $\mathcal O(n^2)$。

代码

const int N = 5e3 + 7;
int n, s[N];
vi e[N];
modint p[N], f[N][N], ans;

void dfs(int x, int fa) {
    f[x][1] = s[x] = 1;
    for (int y : e[x])
        if (y != fa) {
            dfs(y, x);
            for (int i = s[x]; i; i--) {
                modint w = 0;
                for (int j = s[y]; j; j--)
                    f[x][i+j] += f[x][i] * f[y][j], w -= p[j] * f[y][j];
                f[x][i] *= w;
            }
            s[x] += s[y];
        }
}

int main() {
    rd(n);
    p[2] = 1;
    for (int i = 4; i <= n; i += 2) p[i] = p[i-2] * (i - 1);
    for (int i = 1, x, y; i < n; i++) rd(x, y), e[x].pb(y), e[y].pb(x);
    dfs(1, 0);
    for (int i = 1; i <= n; i++) ans += p[i] * f[1][i];
    print(ans);
    return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注