bzoj3622(dp,广义容斥原理)

题面全是图片
https://www.lydsy.com/JudgeOnline/problem.php?id=3622


s o l u t i o n solution solution
题目等价于求 ∑ [ a i > b i ] − [ b i > a i ] = k \sum[a_i>b_i]-[b_i>a_i]=k [ai>bi][bi>ai]=k的方案数
恰好 k k k个,就非常的广义容斥
首先保证两两不同,我们可以转化为求 ∑ [ a i > b i ] = t \sum[a_i>b_i]=t [ai>bi]=t的方案树

可以发现对 a , b a,b a,b排序后,我们可以很轻松的求出上式至少为 t t t的方案数
但是恰好为 t t t貌似不是很好限制
考虑广义容斥的式子
f i = g i − ∑ j = i + 1 n ( j i ) f j f_i=g_i-\sum_{j=i+1}^{n}{j\choose i}f_j fi=gij=i+1n(ij)fj
直接套式子即可

d p [ i ] [ j ] dp[i][j] dp[i][j]求出前 i i i个, j j j个匹配的是比自己小的,其它随意的方案数
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ ( l e s s [ i ] − j + 1 ) dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(less[i]-j+1) dp[i][j]=dp[i1][j]+dp[i1][j1](less[i]j+1)
那么 g i = d p [ n ] [ i ] ∗ ( n − i ) ! g_i=dp[n][i]*(n-i)! gi=dp[n][i](ni)!

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
const int p = 1e9+9;
int n,k;
int a[2010],b[2010],P[2010],fac[2010];
int dp[2010][2010],f[2010],g[2010],c[2010][2010];
inline int min(int a,int b){return a<b?a:b;}
inline int calc(int a,int b){return (a+=b)>=p?a-=p:a;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int del(int a,int b){return (a-=b)<0?a+=p:a;}
int main()
{ 
    n = rd();k = rd();
    if((n-k)%2 != 0) {printf("0\n");return 0;}
	rep(i,1,n) a[i] = rd();rep(i,1,n) b[i] = rd();
	sort(a+1,a+n+1);sort(b+1,b+n+1);
	int j = 0;
	rep(i,1,n) {while(j < n && a[i]>b[j+1])j++;P[i] = j;}
	dp[0][0] = 1;
	rep(i,1,n) rep(j,0,min(i,P[i]))
	 	if(j != 0)dp[i][j] = calc(dp[i-1][j],mul(dp[i-1][j-1],P[i]-j+1));
	    else dp[i][j] = dp[i-1][j];
	rep(i,1,n)
	{
		c[i][0] = c[i][i] = 1;
		rep(j,1,i-1) c[i][j] = calc(c[i-1][j],c[i-1][j-1]);
	}
	fac[0] = 1; rep(i,1,n) fac[i] = mul(fac[i-1],i);
	rep(i,1,n) g[i] = f[i] = mul(dp[n][i],fac[n-i]);
	repp(i,n,1) rep(j,i+1,n) f[i] = del(f[i],mul(f[j],c[j][i]));
	printf("%d\n",f[n/2+(k+1)/2]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值