BZOJ3622 容斥+DP+恰好

版权声明:欢迎转载 https://blog.csdn.net/animalcoder/article/details/82504303

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;
    }

猜你喜欢

转载自blog.csdn.net/animalcoder/article/details/82504303
今日推荐