[DP记录]AT4352 [ARC101C] Ribbons on Tree

command_block

2021-02-13 12:46:16

Personal

**题意** : 给定一棵 $n$ 个节点的树,保证 $n$ 为**偶数**。 给树上的点两两配对,对于一对 $(u,v)$,在树上将 $u\leftrightarrow v$ 的路径染色。 定义一个配对方案合法当且仅当所有边都有颜色。求合法配对方案数,答案对 $10^9+7$ 取模。 $n\leq 5000$ ,时限$\texttt{2s}$。 ------------ 不难想到如下 $\rm DP$ : $f[u][k]$ 表示 $u$ 子树内剩余 $k$ 个节点没有匹配,且匹配路径以及未匹配点到 $u$ 的路径覆盖整个子树的方案数。 边界 : $f[u][0]=1$ 转移 : $f'[u][k]=\sum\limits_{i=1}\sum\limits_{k_1+k_2=k+2i,k_1\geq i,k_2\geq i}f[u][k_1]f[v][k_2]i!$ 复杂度为 $O(n^3)$。 以上 $\rm DP$ 难以优化,改而考虑容斥。 钦定边集 $S$ 不能被覆盖,设方案数为 $c$ ,则贡献为 $c(-1)^{|S|}$。 考虑钦定边集 $S$ 后如何计算 $c$ 。将不能被覆盖的边断掉,每个连通块内都能任意匹配,对于一个大小为 $n$ 的联通块,方案数即为 $W(n)=1\times 3\times 5\dots\times (n-1)$ ,若 $n$ 为奇数方案为 $0$。 于是,记 $f[u][k]$ 表示 $u$ 子树内断边之后 $u$ 所在的联通块大小为 $k$ 的贡献总和。 记 $g[u]$ 为 $u$ 子树内的方案数。 边界 : $f[u][1]=1$ 转移 : $f'[u][k]=-g[v]f[u][k]+\sum\limits_{k_1+k_2=k}f[u][k_1]f[v][k_2]$ 其中, $-g[v]f[u][k]$ 是断边的贡献,后者是不断边的贡献。 $g[u]=\sum\limits_{k=1}f[u][k]\cdot W(k)$ 最终答案即为 $g[1]$。 根据树形背包,复杂度为 $O(n^2)$。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #define pb push_back #define MaxN 5050 using namespace std; const int mod=1000000007; vector<int> g[MaxN]; int ans,f[MaxN][MaxN],t[MaxN],s[MaxN],w[MaxN],siz[MaxN]; void dfs(int u) { siz[u]=1;f[u][1]=1; for (int i=0,v;i<g[u].size();i++) if (!siz[v=g[u][i]]){ dfs(v); for (int k1=1;k1<=siz[u];k1++) for (int k2=1;k2<=siz[v];k2++) s[k1+k2]=(s[k1+k2]+1ll*f[u][k1]*f[v][k2])%mod; siz[u]+=siz[v]; for (int k=1;k<=siz[u];k++) {f[u][k]=(s[k]-1ll*f[u][k]*t[v])%mod;s[k]=0;} } for (int k=1;k<=siz[u];k++) t[u]=(t[u]+1ll*f[u][k]*w[k])%mod; } int n; int main() { scanf("%d",&n); w[2]=1; for (int i=4;i<=n;i+=2) w[i]=1ll*w[i-2]*(i-1)%mod; for (int i=1,u,v;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v);g[v].pb(u); }dfs(1); printf("%d",(t[1]+mod)%mod); return 0; } ```