bzoj3622 已經沒有什麼好害怕的了(容斥原理+DP+二項式反演)

bzoj3622 已經沒有什麼好害怕的了

原題地址http://www.lydsy.com/JudgeOnline/problem.php?id=3622

題意:
這裏寫圖片描述

數據範圍
1≤ n ≤ 2000,0 ≤ k ≤ n

題解:
首先k+n 不是偶數就無解了,讓k=(k+n)/2 ,那麼問題就是:
給兩組n 個數a1...anb1...bn , 保證數字互不不相同. 問有多少種將它們配對的方式, 使得ai>bi 的對數恰好爲k。

如果要算恰好k對的數量,並不好算,但倘若放開範圍算>=k的,就有一個比較簡單的DP:
a[]b[] 從小到大排序,t[i] 表示a 的第i 個比bt[i] 個大。
定義f[i][j] 表示考慮了a1...ai , 在其中選出j 個, 且這j 對都滿足a > b的方案數。
f[i][j]=f[i1][j]+(t[i]j+1)
f[n][i](ni)! 就是>=k的方案數,恰好爲i的會在其中算(ik)
Fi=f[n][i](ni)! ,真實的恰好有i對的方案數是gi ,有:
Fk=i=kn(ik)gi
其實已經可以n2 推了,但是麼根據二項式反演有:
gk=i=kn(1)ik(ik)Fi
就可以直接算了。

代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int mod=1000000009;
const int N=2005;
int n,k,a[N],b[N],f[N][N],C[N][N],t[N],fac[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int j=1;j<=n;j++) scanf("%d",&b[j]);
    if(n<k||(n+k)%2==1) {printf("0\n"); return 0;}
    k=(n+k)/2;
    sort(a+1,a+n+1); sort(b+1,b+n+1);
    fac[0]=1; for(int i=1;i<=n;i++) fac[i]=(1LL*fac[i-1]*i)%mod;
    for(int i=1,j;i<=n;i++) {j=1; while(j<=n&&b[j]<a[i]) j++; t[i]=j-1;}
    for(int i=0;i<=n;i++) 
    for(int j=0;j<=i;j++)
    {
        if(j==0) f[i][j]=1;
        else f[i][j]=(f[i-1][j]+1LL*max(0,t[i]-j+1)*f[i-1][j-1]%mod)%mod;
        if(i==j||j==0) C[i][j]=1;
        else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
    int ans=0;
    for(int i=k;i<=n;i++) 
    {
        int w; if((i-k)%2) w=-1;else w=1;
        int ret=(1LL*C[i][k]*fac[n-i])%mod;
        ret=(1LL*ret*f[n][i])%mod;
        ans=ans+ret*w; if(ans<0) ans+=mod;
        ans%=mod;
    }
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章