[ARC101E] Ribbons on Tree
[ARC101E] Ribbons on Tree
题目描述
给定一个大小为 \(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());
}