• P4859 已经没有什么好害怕的了(dp+二项式反演)


    P4859 已经没有什么好害怕的了


     

    啥是二项式反演(转)

    如果你看不太懂二项式反演(比如我)

    那么只需要记住:对于某两个$g(i),f(i)$

    ----------------------------

    如果:$f(n)=sum_{i=0}^{n}C(n,i)g(i)$

    那么:$g(n)=sum_{i=0}^{n}(-1)^{n-i} C(n,i)f(i)$

    ----------------------------

    如果:$f(k)=sum_{i=k}^{n}C(i,k)g(i)$

    那么:$g(k)=sum_{i=k}^{n}(-1)^{i-k} C(i,k)f(i)$


    糖果比药片能量大的组数比“药片”比“糖果”能量大的组数多k组。看起来很麻烦

    设“药片”比“糖果”能量大的组数为$x$

    $x+(x-k)=n$

    解得$x=(n+k)/2$,所以$n+k$为奇数一定是没有方案的

    现在问题转化成:“药片”比“糖果”能量大的组数为$(n+k)/2$的方案数

    看看数据$O(n^2)$可过,想着搞个dp

    先将$A[i],B[i]$小到大排好序

    蓝后设$f[i][j]$表示(从小到大)扫到$A[i]$(第$i$个),并配好$j$对的方案数

    再预处理好$l[i]$表示$B$数组中有多少个数小于$A[i]$

    分为不配对或配对的情况,列出方程

    $f[i][j]=f[i-1][j]+max(0,l[i]-j+1)*f[i-1][j-1]$

    $dp$完了,现在我们来考虑$f[n][i](0<=i<=n)$有啥用(大雾)

    对于每个$f[n][i]$,剩下没配对的$n-i$个可以随便排序(注意随便排序可能又配出合法对,有重复计算

    于是我们设个$g(i)$表示$n$个数合法配对数$>=i$的排列方案数

    $g(i)=f[n][i]*(n-i)!$

    但是我们要求的是合法配对数$==i$的排列方案数鸭

    设个$f(i)$表示所求↑↑↑

    显然对于每个$g(k) and f(i) (i>=k)$

    $g(k)$都加了$C(i,i-k)=C(i,k)$个$f(i)$进去

    于是我们就得到了$g(k)$关于$f(i)$的表达式

    $g(k)=sum_{i=k}^{n}C(i,k)f(i)$

    仔细一看,这不是可以套二项式反演吗!

    $f(k)=sum_{i=k}^{n}(-1)^{i-k} C(i,k)g(i)$

    蓝后就可以愉快地把$f(k)$算出来辣

    注意$mod=1e9+9$(不是$1e9+7$)TAT

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    #define N 2010
    const ll P=1e9+9;
    int n,k,A[N],B[N],l[N];
    ll f[N][N],g[N],inv[N],fac[N],ifac[N],ans;
    void prep(){
        inv[1]=1; fac[0]=fac[1]=ifac[0]=ifac[1]=1;
        for(ll i=2;i<=n;++i){
            inv[i]=(P-P/i)*inv[P%i]%P;
            fac[i]=fac[i-1]*i%P;
            ifac[i]=ifac[i-1]*inv[i]%P;
        }
    }
    inline ll C(int a,int b){return fac[a]*ifac[b]%P*ifac[a-b]%P;}
    int main(){
        scanf("%d%d",&n,&k);
        if((n+k)&1){puts("0"); return 0;}
        k=(n+k)/2; prep();
        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+n+1); sort(B+1,B+n+1); int tmp=0;
        for(int i=1;i<=n;++i){
            while(tmp<n&&B[tmp+1]<A[i]) ++tmp;
            l[i]=tmp;
        }f[0][0]=1;
        for(int i=1;i<=n;++i){
            f[i][0]=f[i-1][0];
            for(int j=1;j<=i;++j)
                f[i][j]=(f[i-1][j]+1ll*(l[i]-j+1)*f[i-1][j-1])%P;
        }
        for(int i=0;i<=n;++i) g[i]=f[n][i]*fac[n-i]%P;
        for(int i=k;i<=n;++i)
            ans=((ans+1ll*(((i-k)&1)?-1:1)*C(i,k)*g[i])%P+P)%P;
        printf("%lld",ans);
        return 0;
    }

     

  • 相关阅读:
    nginx 服务企业应用
    3D模型展示以及体积、表面积计算
    php实现MySQL读写分离
    three.js实现3D模型展示
    thinkphp5.1+think-queue
    GIT记住远端仓库地址密码
    php实现采集(仅做参考)
    phpStudy集成环境apche+openssl配置本地https
    HTTP与HTTPS区别
    在父页面用Iframe加载子页面时,将父页面的title替换成子页面title
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10785850.html
Copyright © 2020-2023  润新知