[ARC101E] Ribbons on Tree

发布时间 2024-01-06 20:09:04作者: Hanx16Msgr

[ARC101E] Ribbons on Tree

Luogu ARC101E

题目描述

给定一个大小为 \(n\) 的树,保证 \(n\) 为偶数且小于 \(5000\)

您需要给树上的点两两配对,对于一组对子 \((u,v)\),在树上将 \(u\to v\) 的路径染色,定义一个配对方案合法当且仅当所有边都有颜色。

求方案数对 \(10^9+7\) 取模。

\(\begin{array}{l}2\le N\le 5000\\2\mid N\\\text{保证输入的一定是一棵树}\end{array}\)

Solution

直接 DP 做是 \(\mathcal O(n^3)\) 的,所以考虑容斥。

考虑不合法的方案数。如果一条边没有被任何配对点覆盖,那么就将这条边删除。对于一个大小为 \(2k\) 不一定连通的块,总共的方案数为 \(g(k)=\displaystyle\prod\limits_{i=1}^k(2i-1)\)。设 \(f(i,j)\) 表示在 \(i\) 子树中,\(i\) 所在连通块的大小为 \(j\) 的方案数(带上容斥系数),这明显是一个书上背包的转移方式。考虑枚举当前儿子到 \(i\) 的这条边是否被断开,那么有:

  • 若断开这条边,那么 \(f(i,j)\gets f(i,j)\cdot f(v,k)\cdot (-g(k))\)
  • 若不断开这条边,那么 \(f(i,j+k)\gets f(i,j)\cdot f(v,k)\)

对于答案,考虑枚举根节点所在连通块的大小,得到答案为 \(\displaystyle\sum\limits_{i=1}^nf(1,i)\cdot g(i)\)

时间复杂度 \(\mathcal O(n^2)\)

Code
mint f[_N][_N], g[_N], h[_N];
int N, siz[_N];
vector<int> e[_N];
void init(int n) {
    g[0] = 1;
    For(i, 1, n) g[i*2] = g[i*2-2] * (2 * i - 1);
}
void dfs(int x, int fa) {
    siz[x] = 1, f[x][1] = 1;
    for (int v: e[x]) if (v ^ fa) {
        dfs(v, x);
        For(i, 1, siz[x]) h[i] = f[x][i], f[x][i] = 0;
        Rof(i, siz[x], 1) Rof(j, siz[v], 1) {
            f[x][i] -= h[i] * f[v][j] * g[j];
            f[x][i+j] += h[i] * f[v][j];
        }
        siz[x] += siz[v];
    }
}
void _() {
    cin >> N;
    For(i, 2, N) {
        int x, y; cin >> x >> y;
        e[x].epb(y), e[y].epb(x);
    }
    init(N / 2), dfs(1, 0);
    mint ans = 0;
    For(i, 1, N) ans += f[1][i] * g[i];
    fmtout("{}\n", ans.val());
}