正题
题目链接:
https://www.luogu.com.cn/problem/AT4352
https://atcoder.jp/contests/arc101/tasks/arc101_c
题目大意
nnn个点之间两两配对,要求配对点之间的路径覆盖整棵树,求方案数
解题思路
考虑容斥,我们钦定有lll条边没有路径覆盖,就有dpdpdp状态fi,j,lf_{i,j,l}fi,j,l表示iii的子树中,目前该子树的联通块大小为jjj,已经切断了lll条边,我们发现该状态已经是O(n3)O(n^3)O(n3)的,显然无法通过。
考虑优化,每个dpdpdp状态的容斥系数是(−1)l(-1)^l(−1)l,所以我们可以不用记录lll这一维度,之间用状态表示乘上了容斥系数的值。
那么我们就有dpdpdp方程,定义calc(x)calc(x)calc(x)表示xxx个点两两匹配的值
(−1)∗fx,i∗fy,j∗calc(j)→fx,i(-1)*f_{x,i}*f_{y,j}*calc(j)\rightarrow f_{x,i}(−1)∗fx,i∗fy,j∗calc(j)→fx,i
fx,i∗fy,j→fx,i+jf_{x,i}*f_{y,j}\rightarrow f_{x,i+j}fx,i∗fy,j→fx,i+j
该方程的复杂度就是每个子树的乘积,可以理解为每个点堆之间进行一次贡献,时间复杂度O(n2)O(n^2)O(n2)
calc(x)=∏i=1x−1[i&1==1]icalc(x)=\prod_{i=1}^{x-1}[i\&1==1]icalc(x)=∏i=1x−1[i&1==1]i
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5100,XJQ=1e9+7;
struct node{ll to,next;
}a[N*2];
ll n,tot,ls[N],siz[N],f[N][N],ans,g[N],fac[N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;
}
void dp(ll x,ll fa){siz[x]=f[x][1]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dp(y,x);for(ll i=1;i<=siz[x];i++)g[i]=f[x][i],f[x][i]=0;for(ll j=1;j<=siz[x];j++){for(ll k=1;k<=siz[y];k++){(f[x][j+k]+=f[y][k]*g[j]%XJQ)%=XJQ;if((k&1)==0)(f[x][j]+=XJQ-f[y][k]*g[j]%XJQ*fac[k-1]%XJQ)%=XJQ;}}siz[x]+=siz[y];}return;
}
int main()
{scanf("%lld",&n);for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}fac[0]=fac[1]=1;for(ll i=2;i<=n;i++)fac[i]=fac[i-2]*i%XJQ;dp(1,1);for(ll i=2;i<=n;i++)ans=(ans+f[1][i]*fac[i-1]%XJQ)%XJQ;printf("%lld",ans);
}