[ARC101C] Ribbons on Tree

神仙的容斥题与神仙的树形DP题。

首先搞一个指数级的做法:求总的、能够覆盖每一条边的方案数,通过容斥可以得到( ext{ans}=sumlimits_E{(-1)^{|E|}F(E)})。其中,(F(E))表示钦定删除边集(E)后,其他的连边方案数。显然经过删边操作,这张图被划分成了很多联通块,联通块之间没有连边,方案数就是每个联通快的方案数的成绩。特别地,当(|E|=0)时,这个是总情况数。

如何求解一个联通块内的连边方案数呢?先上式子:( ext{ret}=1 imes 3 imes 5 imes cdots imes (n - 3) imes (n - 1))。如何理解这个式子呢?首先这个“联通块”并不是一定要求联通,因为我容斥的时候只要求某个边集断开,并没有要求其他的都联通呀!所以,随便找第一个点,它可以和(n-1)个点匹配;然后找下一个点,它可以和(n-3)个点匹配······最后总方案树就是它们的乘积。

现在我们有了指数级做法了(就是枚举(E),时间复杂度(Theta(n^2))),现在来尝试把它优化到多项式时间复杂度。

一个“联通块”的情况数,仅仅与“点数”的点数有关,这启示我们在 DP 中存下一维状态来存当前点集的大小;每条边都只有选和不选两种情况,这让我们联想到了 0/1 背包。

(f_{u,i,0/1}) 表示,(u)这个子树,跟(u)结点连着的“联通块”大小是(i),此时还未乘上(i)这个联通块的情况数的总情况数(也就是把这个子树删到只剩下(i)个点,不考虑(i)内部情况的总情况数)。显然这时(f_{u,i,0/1})再乘上(g[i])就是总情况数((g[i]=1 imes 3 imes cdots imes (i - 3) imes (n - 1)))。

转移的时候,考虑怎么合并把(v)合并到(father_v)上面;我们的目的是优化(Theta(n^2))枚举边集,所以看这条边钦定删掉和不钦定删掉两种情况,方程分别是:

[f_{v,j,a} * f_{father,i,b} ightarrow f^{'}_{father,i+j,aoplus b} ]

[f_{v,j,a} * f_{father,i,b} * g[j] ightarrow f^{'}_{father,i,aoplus boplus 1} ]

上面那个方程的含义是,不钦定删除 <v,fat> 那条边,那么它们下面的总情况数是它们的乘积;下面那个方程的意思是,不选这条边,那么下面的方案数要乘上(v,b)的总方案数,即(f imes g)

最终的答案就等于(sumlimits_{i=1}^n{f_{1,i,0} - f_{1, i, 1}})

总结一下,这里有几个重要的点:

1.转化为容斥的问题。

2.结合情况数只与结点数有关和边的出现与否只有两种情况,设计类似于 0/1 背包的 DP 方法。

3.树形 DP 不一定是考虑怎么从子节点们一次性推出父亲,还可以考虑怎么把子节点依次并进父亲的答案里面,这种 DP 的实现,需要把 DP 数组的其中一维拷贝一遍,具体实现看下面:

#include <bits/stdc++.h>
#define LL long long

using namespace std;
const int maxn = 5e3 + 1e2;
const int mod = 1e9 + 7;

int n, f[maxn][maxn][2], g[maxn], sze[maxn];

vector<int> T[maxn];

void dfs(int x, int fa)
{
	int tmp[maxn][2];
	memset(tmp, 0, sizeof tmp);
	//cout << "HH" << endl;
	//cout << x << endl;
	sze[x] = 1;
	f[x][1][0] = 1;
	for (auto s : T[x])
	{
		if (s == fa) continue;
		dfs(s, x);
		for (int i = sze[x]; i; i--)
		{
			for (int j = sze[s]; j; j--)
			{
				for (int a = 0; a < 2; a++)
				{
					for (int b = 0; b < 2; b++)
					{
						(tmp[i + j][a ^ b] += (LL)f[x][i][a] * f[s][j][b] % mod) %= mod;
						(tmp[i][a ^ b ^ 1] += (LL)f[x][i][a] * f[s][j][b] % mod * g[j] % mod) %= mod;
					}
				}
			}
		}
		sze[x] += sze[s];
		for (int i = 0; i <= sze[x]; i++)
		{
			for (int j = 0; j < 2; j++)
			{
				f[x][i][j] = tmp[i][j];
				tmp[i][j] = 0;
			}
		}
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int u, v;
		cin >> u >> v;
		T[u].push_back(v);
		T[v].push_back(u);
	}
	g[0] = 1;
	for (int i = 2; i <= n; i += 2)
	{
		g[i] = (LL)g[i - 2] * (i - 1) % mod;
	}
	dfs(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++)
	{
		(ans += ((LL)f[1][i][0] - f[1][i][1] + mod) * g[i] % mod) %= mod;
	}
	cout << ans << endl;
	
	return 0;
}


as 0.4123
原文地址:https://www.cnblogs.com/Linshey/p/13894374.html