33.BZOJ3622 容斥定理+DP+恰好
题意:
各有n个数an,bn,上下11匹配,问恰好ai>bi 比ai<bi多k组的方案数
等价于问恰好ai>bi 有(n+k)/2组的方案数
恰好->至少 ,先求至少 :
设dp[i][j]为前i个数至少j组满足 a[x]>b[y]的方案数
Dp[i][j]=dp[i-1][j](第i个不匹配小的) +dp[i-1][j-1]*(nxt[i]-j+1)
Nxt[i]表示a[i]有多少个b[j]比a[i]小,边界dp[i][0]=1
恰好K组等于至少K组*容斥因子-至少K+1组+至少K+2组…..(套路)
至少K组=dp[n][k]*(n-k)! (满足性质,剩下乱选) ,C【i,k】是套路容斥因子
就这题由于有DP表示前i个,就不能像之前那样先选K个剩下套容斥了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+9;
ll c[2005][2005];ll dp[2005][2005];
int nxt[2005];int a[2005];int b[2005];ll jc[2005];
inline ll add(ll a,ll b)
{
if(a+b>mod)return (a+b)%mod;
return a+b;
}
void init()
{
c[0][0]=1;jc[0]=1;
for(int i=1;i<=2000;i++)c[i][0]=c[i][i]=1,c[i][1]=i,jc[i]=jc[i-1]*i%mod;
for(int i=1;i<=2000;i++)
for(int j=1;j<=i;j++)
c[i][j]=add(c[i-1][j-1],c[i-1][j]);
//c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
int main()
{
init();
int n,k;scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
sort(a+1,a+1+n);sort(b+1,b+1+n);
int poi=0;
for(int i=1;i<=n;i++)//找a[i]有多少个b[j]满足ai>bj
{
while(poi<=n&&b[poi]<a[i])poi++;
nxt[i]=poi-1;
}
//for(int i=1;i<=n;i++)printf("%d %d\n",i,nxt[i]);
dp[0][0]=1;k=(n+k)/2;
for(int i=1;i<=n;i++)//第i个至少j个满足a[x]>b[y]
{
dp[i][0]=1;
for(int j=1;j<=i;j++)
{
dp[i][j]=add(dp[i-1][j],1ll*dp[i-1][j-1]*max(nxt[i]-j+1,0) );
//选或不选
dp[i][j]%=mod;
}
}
ll ans=0;int cnt=1;
for(int i=k;i<=n;i++)//就那个公式
{
if(cnt&1) ans=add(ans,dp[n][i]*jc[n-i]%mod*c[i][k]);
else ans=(ans-dp[n][i]*jc[n-i]%mod*c[i][k]%mod+mod)%mod;
cnt^=1;
}