AT4352-[ARC101C] Ribbons on Tree【dp,容斥】

正题

题目链接:
https://www.luogu.com.cn/problem/AT4352
https://atcoder.jp/contests/arc101/tasks/arc101_c


题目大意

n n n个点之间两两配对,要求配对点之间的路径覆盖整棵树,求方案数


解题思路

考虑容斥,我们钦定有 l l l条边没有路径覆盖,就有 d p dp dp状态 f i , j , l f_{i,j,l} fi,j,l表示 i i i的子树中,目前该子树的联通块大小为 j j j,已经切断了 l l l条边,我们发现该状态已经是 O ( n 3 ) O(n^3) O(n3)的,显然无法通过。

考虑优化,每个 d p dp dp状态的容斥系数是 ( − 1 ) l (-1)^l (1)l,所以我们可以不用记录 l l l这一维度,之间用状态表示乘上了容斥系数的值。

那么我们就有 d p dp dp方程,定义 c a l c ( x ) calc(x) calc(x)表示 x x x个点两两匹配的值
( − 1 ) ∗ f x , i ∗ f y , j ∗ c a l c ( j ) → f x , i (-1)*f_{x,i}*f_{y,j}*calc(j)\rightarrow f_{x,i} (1)fx,ify,jcalc(j)fx,i
f x , i ∗ f y , j → f x , i + j f_{x,i}*f_{y,j}\rightarrow f_{x,i+j} fx,ify,jfx,i+j
该方程的复杂度就是每个子树的乘积,可以理解为每个点堆之间进行一次贡献,时间复杂度 O ( n 2 ) O(n^2) O(n2)

c a l c ( x ) = ∏ i = 1 x − 1 [ i & 1 = = 1 ] i calc(x)=\prod_{i=1}^{x-1}[i\&1==1]i calc(x)=i=1x1[i&1==1]i


c o d e code code

#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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值