zoukankan      html  css  js  c++  java
  • 【扩展lucas定理】

    洛谷模板题面:https://www.luogu.org/problemnew/show/P4720

    扩展卢卡斯被用于解决模数为合数情形下的组合数问题。
    首先我们把模数mod质因数分解,解决模每个素数的幂意义下的组合数这样一个子问题,最后用crt把他们合并到一起。
    那么我们现在要解决这样一个问题:

    [C(n,m) quad mod quad p^k ]

    其中p为质数。
    (p^k)可能很大,而且性质与p不同,使用单纯的lucas解决肯定是不行了。
    我们考虑把组合数拆成阶乘的形式,发现 $n! ,m! , (n-m)! $都有可能含有质数p,而当分母含有p的时候与模数不互质,逆元是没有办法求的,所以我们必须把p全都提出。
    化成这种形式:

    [frac{frac{n!}{p^a}}{frac{m!*(n-m)!}{p^b}}*p^{(a-b)} ]

    发现 去除掉所有p的(n!) 是有非常美妙的性质的,它可以提出一段可求长度的去p阶乘,然后剩下一部分是更小规模的阶乘(读者可以试着导一导),有了这个性质,我们便可以递归求解了。预处理出来一些东西后,可以像普通lucas一样简洁,高效。

    细节部分详见代码中分解质因数时的预处理部分和Fac函数。

    注意:

    1、这里求逆元要用exgcd。

    2、复杂度与min(n,max_p)有关,当mod比较大n较小时别忘了取min。

    接下来是一份AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N =1000005;
    #define rep(i,a,b) for(register int i=(a);i<=(b);++i)
    
    typedef long long ll;
    ll m,n;
    int mod;
    ll fac[N],inv[N];
    ll ksm(ll x,ll y,ll M){
        ll aa=1ll;
        for(x%=M;y;y>>=1,x=(x*x)%M)if(y&1)aa=(aa*x)%M;
        return aa;
    }
    int p[N],pk[N],cnt;
    ll sum,fak[22][N];
    ll exgcd(ll x,ll y,ll &a,ll &b){
        if(!y){a=1,b=0;return x;}
        ll d=exgcd(y,x%y,b,a);
        b-=x/y*a;
        return d;
    }
    inline ll Inv(ll x,ll y){
        ll inv,rua;
        exgcd(x,y,inv,rua);
        return (inv+y)%y;
    }
    ll Fac(ll x,int i){
        if(x==0||x==1)return 1;
        return Fac(x/p[i],i)*ksm(fak[i][pk[i]-1],x/pk[i],pk[i])%pk[i]*fak[i][x%pk[i]]%pk[i];
    }
    ll ex_Lucas(ll x,ll y,int i){
        if(x<y)return 0;
        ll num=0;
        for(ll j=x;j;j/=p[i])
        num+=j/p[i];
        for(ll j=y;j;j/=p[i])num-=j/p[i];
        for(ll j=x-y;j;j/=p[i])num-=j/p[i];
        return Fac(x,i)*Inv(Fac(y,i),pk[i])%pk[i]*Inv(Fac(x-y,i),pk[i])*ksm(p[i],num,pk[i])%pk[i];
    }
    ll ans;
    int main(){
        scanf("%lld%lld%d",&n,&m,&mod);
        int x=mod;
        for(int i=2;i*i<=mod;++i){
            if(x%i==0){
                p[++cnt]=i;
                pk[cnt]=1;
                while(x%i==0)x/=i,pk[cnt]*=i;
                sum=1;fak[cnt][0]=1;
                rep(j,1,pk[cnt]-1){
                    if(j%p[cnt])sum=sum*j%pk[cnt];
                    fak[cnt][j]=sum;
                }
            }
        }
        if(x!=1){
            ++cnt,p[cnt]=pk[cnt]=x;
            sum=1;fak[cnt][0]=1;
            rep(j,1,pk[cnt]-1){
                if(j%p[cnt])sum=sum*j%pk[cnt];
                fak[cnt][j]=sum;
            }
        }
        ll tmp;
        rep(i,1,cnt){
            tmp=ex_Lucas(n,m,i);
            ans=(ans+tmp*(mod/pk[i])%mod*Inv(mod/pk[i],pk[i])%mod)%mod;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    自动滑块验证登录QQ-java实现
    今日校园自动提交问卷-Java实现
    文库下载实现自动化
    测试
    软件工程结课小结
    结对项目-java生成四则运算
    JS判断对象为空的三种方法
    vue 组件间 8 大通讯方式 之三 eventBus
    vue 组件间 8 大通讯方式 之二 provide/ inject ref / refs
    vue 组件间 8 大通讯方式 之一 props / $emit $children / $parent
  • 原文地址:https://www.cnblogs.com/Sinuok/p/11129587.html
Copyright © 2011-2022 走看看