已经没有什么好害怕的了——二项式反演+经典套路

题面:已经没有什么好害怕的了 

首先,大k个,k=(n+k)/2,糖果多的恰好有k组

一个通用技巧是:

找到两个数组f,g

f范围宽松好统计,g范围严格难统计但是和答案有直接关系,

这样,只要得到f和g的关系,就可以找到答案!

经常是可以得到f由g的表达式,然后斯特林反演或者二项式反演得到g的求法

也可以用多项式科技

数论函数的反演也可以这么做。

 

这个题就这样做:

涉及大小关系,先把A,B从小到大排序便于决策

称糖果比药片大的配对叫“优秀”

设f[k]表示,“钦定选择k组优秀,其他任意选”方案数。

g[k]表示,“恰好k组优秀”。g[K]就是答案

f和g的关系式:

$f[i]=\sum_{j=i}^nC(j,i)g[j]$

二项式反演:

$g[i]=\sum_{j=i}^n(-1)^{j-i}C(j,i)*f[j]$

任意选择i个都构成一组钦定。

计算f

DP,$f[i][j]$前i个,钦定了j个

$f[i][j]=f[i-1][j]+f[i-1][j-1]*(small[i]-(j-1))$

第i个选择或者不选择,不选择先不给予匹配,选择,就从比$a_i$小的$b_i$中选择,排好序了,所以直接$-(j-1)$就是剩下的

最后再给其他没有给予匹配的随便给予:$f[i]*=(n-i)!$

然后根据反演式子。

或者,如果有时难以反演,就倒着求出每个g[i],用g[i+1~n]和f[i]得到g[i]

 

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int mod=1e9+9;
const int N=2002;
int n,k;
int a[N],b[N];
int f[N][N],g[N];
int C[N][N];
int qm(int x,int y){
    int ret=1;
    while(y){
        if(y&1) ret=(ll)ret*x%mod;
        x=(ll)x*x%mod;
        y>>=1;
    }
    return ret;
}
int main(){
    rd(n);rd(k);
    if((n+k)&1){
        puts("0");return 0;
    }
    k=(n+k)>>1;
    for(reg i=1;i<=n;++i) rd(a[i]);
    for(reg j=1;j<=n;++j) rd(b[j]);
    sort(a+1,a+n+1);sort(b+1,b+n+1);
    int smal=0;
    f[0][0]=1;
    for(reg i=1;i<=n;++i){
        while(smal<n&&b[smal+1]<a[i])++smal;
        for(reg j=0;j<=i;++j){
            if(j&&smal>(j-1))f[i][j]=((ll)f[i-1][j-1]*(smal-(j-1)))%mod;
            f[i][j]=(f[i][j]+f[i-1][j])%mod;
        //    cout<<" i j "<<i<<" "<<j<<" : "<<f[i][j]<<endl;
        }
        
    }
    int jie=1;
    for(reg j=n;j>=0;--j){
        if(n-j)jie=(ll)jie*(n-j)%mod;
        f[n][j]=((ll)f[n][j]*jie)%mod;
    //    cout<<" ff "<<f[n][j]<<endl;
    }
    C[0][0]=1;
    for(reg i=1;i<=n;++i){
        C[i][0]=1;
        for(reg j=1;j<=n;++j){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
    }
    for(reg i=n;i>=k;--i){
        if(i==n){
            g[i]=f[n][i];
        }else{
            int sum=0;
            for(reg j=i+1;j<=n;++j){
                sum=(sum+(ll)C[j][i]*g[j]%mod)%mod;
            }
            g[i]=(ll)((ll)f[n][i]-sum+mod)%mod;
        }
    }
    printf("%d",g[k]);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/2/18 8:32:07
*/

 

posted @ 2019-02-18 09:15  *Miracle*  阅读(394)  评论(0编辑  收藏  举报