• 组合数总结


    ☆组合数的若干求法☆

    一、杨辉三角法

    公式:(C_m^n=C_{m-1}^n+C_{m-1}^{n-1})

    要求:无

    复杂度:(O(N^2))~(O(1))

    适用性:较广

    二、逆元法

    公式:(C_m^n=frac{m!}{n! imes (m-n)!})

    要求:模数(p)为质数

    复杂度:(O(logp+m))(计算逆元+预处理阶乘)

    预处理后可做到:(O(m))~(O(1))

    适用性:(m)较小时

    三、lucas定理+逆元法

    公式:

    (C_m^n=frac{m!}{n! imes (m-n)!})

    (C_m^n equiv C_{m/p}^{n/p} imes C_{m \% p}^{n \% p} (mod p))

    要求:模数(p)为质数

    复杂度:(O(logmlogp+p))

    预处理后可做到:(O(p))~(O(logm))

    适用性:(p)较小时,(m)较大时

    四、lucas定理+逆元+CRT合并法

    公式同三

    要求:模数分解质因数后,所有的质因子指数都为(1)

    具体的:我们在每一个质因子下的答案,然后适用CRT进行合并

    复杂度:(O(tlogmaxplogm+tlogp))(maxp为p的最大质因子,t是质因子个数,后面一项是玄学,假装是CRT合并的复杂度)

    预处理后:(O(tlogmaxp))~(O(tlogm))

    适用性:小,但(p,m,n)可以较大且(p)可以不是质数

    五、扩展lucas

    要求:无

    到这里,我们把上面所有的求法都抛开吧,不一样不一样的,别被误导了

    看看最开始的东西,求

    (frac{m!}{n! imes (m-n)!} mod p)

    先把(p)唯一分解掉

    (p=prod_{i=1}^t p_i^{c_i})

    如果我们可以求出每一个(frac{m!}{n! imes (m-n)!} mod p_i^{c_i}),那么很显然是可以CRT合并的。

    问题转换成了如何求

    (frac{m!}{n! imes (m-n)!} mod p_i^{c_i})

    我们考虑把计算式中所有含(p_i)的项给搞出来,然后拿到一边去

    拿走的一部分直接快速幂计算,没拿走的一部分也可以直接求逆元了

    我们考虑 没拿走的一部分的计算 和 含(p_i)的项的个数的计算公式

    首先考虑(n!)中含有多少个(p_i)

    (f(n))代表(n!)中含(p_i)的个数

    (n!=1 imes 2 imes 3 imes ... imes n)

    我们把乘积项中所有含(p_i)的项取出来,这些项一定是(1 imes p_i),(2 imes p_i)...(lfloor n/p_i floor imes p_i),对每一项取出一个(p_i),则剩下的就是(lfloor n/p_i floor !),很好子问题

    我们得到了递归计算的公式:

    (f(x)=f(lfloor x/p_i floor)+lfloor x/p_i floor)

    (f(0)=0)

    然后我们考虑如何计算

    (frac{n!}{f(n)*p_i})

    我们把乘积项按(p_i^{c_i})分成一节一节的,然后把所有的(p_i)的倍数项拿走

    因为(x equiv x+p_i^{c_i} (mod p_i^{c_i})),所以每一节的乘积是一样的,快速幂直接算,剩下的不足一个循环节的暴力算

    然后我们算(p_i)的倍数项,发现同样的,我们把每一项拿走一个,就剩下了子问题,真是皆大欢喜,问题就解决啦

    分析一下复杂度

    求除过以后的阶乘(O(log_{p_i}m+p_i^{c_i}))

    求质因子个数(O(log_{p_i}m))

    求逆元和CRT合并的复杂度应该比较低。。

    大概就是这样 实际上是算不下去啦

    适用性:非常广,但是不太好打

    Code:

    #include <cstdio>
    #define ll long long
    ll mod;
    ll quick_pow(ll d,ll k,ll p)
    {
        ll f=1;
        while(k)
        {
            if(k&1) f=f*d%p;
            d=d*d%p;
            k>>=1;
        }
        return f;
    }
    ll fac(ll n,ll p,ll d)
    {
        if(!n) return 1;
        ll f=1,res=1;
        for(ll i=2;i<=p;i++)
            if(i%d) (f*=i)%=p;
        for(ll i=2;i<=n%p;i++)
            if(i%d) (res*=i)%=p;
        return fac(n/d,p,d)*quick_pow(f,n/p,p)%p*res%p;
    }
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1,y=0;
            return;
        }
        exgcd(b,a%b,x,y);
        ll tmp=x;
        x=y;
        y=tmp-a/b*y;
    }
    ll inv(ll n,ll p)
    {
        ll x,y;
        exgcd(n,p,x,y);
        return (x%p+p)%p;
    }
    ll C(ll n,ll m,ll p,ll d)
    {
        ll ct=0;
        for(ll i=m;i;i/=d) ct+=i/d;
        for(ll i=n;i;i/=d) ct-=i/d;
        for(ll i=m-n;i;i/=d) ct-=i/d;
        return fac(m,p,d)*inv(fac(n,p,d),p)%p*inv(fac(m-n,p,d),p)%p*quick_pow(d,ct,p)%p;
    }
    ll CRT(ll a,ll b)
    {
        return mod/b*a%mod*inv(mod/b,b)%mod;
    }
    ll exlucas(ll n,ll m,ll p)
    {
        ll ans=0;
        for(ll d=1,i=2;i*i<=p;d=1,i++)
        {
            if(p%i==0)
            {
                while(p%i==0)
                {
                    p/=i;
                    d*=i;
                }
                (ans+=CRT(C(n,m,d,i),d))%=mod;
            }
        }
        if(p!=1)
            (ans+=CRT(C(n,m,p,p),p))%=mod;
        return ans;
    }
    int main()
    {
        ll m,n;
        scanf("%lld%lld%lld",&m,&n,&mod);
        printf("%lld
    ",exlucas(n,m,mod));
        return 0;
    }
    
    

    总结:灵活使用吧,最好都掌握一下


    2018.8.27

  • 相关阅读:
    配置Log4j(非常具体)
    System.Net.WebClient.cs
    Code:获取指定汉字的首字母
    DBS:目录
    Jasper:推送 API
    Jasper-template
    Code:Base64 编码/解码
    DCloud-HTML5+:5+ App开发入门指南
    DCloud-HTML5+:barcode
    Nuget-QRCode:QRCoder
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9540250.html
Copyright © 2020-2023  润新知