← 全部文章

ARC101E Ribbons on Tree 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

ARC101E Ribbons on Tree

题意

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

题解

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

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

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

转移就是树上背包,时间复杂度 O(n2)\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;
}

标签:DP、容斥、树形 DP、组合计数、背包

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…