zoukankan      html  css  js  c++  java
  • 已经没有什么好害怕的了——二项式反演+经典套路

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

    首先,大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
    */
  • 相关阅读:
    Linux 命令汇总总结相关
    数据结构---python---表
    python-----Queue模块
    再看python多线程------threading模块
    <转> Struct 和 Union区别 以及 对内存对齐方式的说明
    python装饰器
    HTTP权威指南----缓存
    HTTP权威指南----连接管理
    以python理解Linux的IO多路复用,select、poll、epoll
    <转载> pycharm快捷键及一些常用设置
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10393784.html
Copyright © 2011-2022 走看看