• [BZOJ 2111] 排列计数


    Link:

    BZOJ 2111 传送门

    Solution:

    小根堆的模型还是很容易能看出来的

    利用树形$dp$统计方案数:$dp[i]=dp[lc]*dp[rc]*C[sz[i]-1][sz[lc]]$

    (小根堆的个数只受序列中数的大小关系影响,与其差值无关,因此每一个组合产生相同的个数)

    其中组合数的计算全是坑点啊……

    需要注意此题中$MOD$的值可能小于$n$

    因此直接求组合数以及用费马小定理算逆元的方法都是错误的($fac[i](i>MOD)$均为0!)

    于是需要使用$Lucas$定理来计算组合数,来保证$a,b$均小于$MOD$时才调用$fac$和$inv$

    此时$fac$和$inv$的上界都应设为$MOD-1$来保证正确性(或者使用另一种线性求1至$n$逆元的方式)

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define lc (i<<1)
    #define rc (i<<1|1)
    typedef long long ll;
    const int MAXN=1e6+10;
    int sz[2*MAXN];
    ll n,MOD,fac[MAXN],inv[MAXN],dp[MAXN];
    
    ll quick_pow(ll a,ll b)
    {
        ll ret=1;
        for(;b;b>>=1,a=a*a%MOD)
            if(b&1) ret=ret*a%MOD;
        return ret;
    }
    
    ll C(ll a,ll b) //n大于MOD时一定要用Lucas 
    {    
        if(a<b) return 0;
        if(a<MOD&&b<MOD) return fac[a]*inv[b]%MOD*inv[a-b]%MOD;
        return C(a/MOD,b/MOD)*C(a%MOD,b%MOD)%MOD;
    }
    
    int main()
    {
        scanf("%lld%lld",&n,&MOD);
        fac[0]=1;
    /*    
        int mn=min(n,MOD-1); 如果n>MOD则要重新设置上限 
        for(int i=1;i<=mn;i++) fac[i]=fac[i-1]*i%MOD;
        inv[mn]=quick_pow(fac[mn],MOD-2);
        for(int i=mn-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%MOD;
    */    
        for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
        inv[0]=inv[1]=1;//求1-n逆元的线性做法
        for(int i=2;i<=n;i++) inv[i]=(MOD/i+1)*inv[i-MOD%i]%MOD;
        for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
        
        for(int i=n;i>=1;i--)
        {
            sz[i]=sz[lc]+sz[rc]+1;
            dp[i]=(lc>n?1:dp[lc])*(rc>n?1:dp[rc])%MOD*C(sz[i]-1,sz[lc])%MOD;
        }
        printf("%lld",dp[1]);
        return 0;
    }

    Review:

    (1)遇到模数要输入的题目要敏感,考虑模数是否可能小于$n$

    此时必须要利用$Lucas$定理求解组合数,求逆元时上限也要从$n$改为$MOD-1$

    (2)小根堆的个数只受序列中数的大小关系影响,与其差值无关

    每一个数值两两不等的序列能产生的小根堆数相同

  • 相关阅读:
    Kafka与RabbitMQ区别
    Illegal instruction 问题的解决方法
    Debian 6 , 十个串口为什么只识别到了 6个 剩下4 个被禁止了
    微信二次认证 C#
    修改XtraMessageBox的内容字体大小
    svn: E155017: Checksum mismatch while updating 校验错误的解决方法
    再生龙恢复分区后修复引导或debian linux修复引导 三部曲
    Clonezilla制作镜像时报错: errextfsclone.c:bitmap free count err
    我用windows live Writer 写个日志试试看
    Debian下签名无法验证
  • 原文地址:https://www.cnblogs.com/newera/p/9291820.html
Copyright © 2020-2023  润新知