zoukankan      html  css  js  c++  java
  • 组合数取模

    1.n,m<=1000 p随意

       暴力

    2.n,m<=10^6  p<=10^9 素数合数无影响

       分解质因数,然后快速幂 

       如果p是素数,且多次询问可以预处理阶乘的模以及阶乘的逆元

    3.n,m<=10^9  p<=10^5且是质数

       lucas定理  多次询问也可以预处理

    4.n,m<=10^9  p<=10^5且是合数

       目前还不会做系列。。。

       我们把p拆成 pi^ci 然后对每个pi求模,最后CRT(中国剩余定理)合并一下。

       考虑如何求这个。

       其实我们只要考虑n!mod p^c即可

       然后我们发现:

        n! = [ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17 * 19 ] * (3 * 6 * 9 * 12 * 15 * 18)= [ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17* 19 ] * 3^6( 1 * 2 * 3 * 4 * 5 * 6)

        把p单独处理之后发现前面是以p^c为周期的(显然),然后后面划归成了n/p,可以递归求解。然后p的次数就是n/p。

        这样我们就可以预处理阶乘,将是p的倍数的位数省略,以1代过。最后p快速幂一下就好。在分子上的话其实一样,只需要预处理成阶乘的逆元即可。

       说起来很麻烦,还是看代码比较好。。。

    void divide(int n)
    {
        for(int i=2;i*i<=n;i++)if(n%i==0)
        {
            p[++cnt]=i;pc[cnt]=1;
            while(n%i==0)pc[cnt]*=i,n/=i;
        }
        if(n>1)p[++cnt]=n,pc[cnt]=n;
    }
    inline ll power(ll x,ll y,ll p)
    {
        ll t=1;
        for(;y;y>>=1,x=x*x%p)
            if(y&1)t=t*x%p;
        return t;
    }
    inline void exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b){x=1;y=0;return;}
        exgcd(b,a%b,x,y);
        ll t=x;x=y,y=t-a/b*y;
    }
    inline ll inv(ll a,ll b)
    {
        ll x,y;
        exgcd(a,b,x,y);
        return (x%b+b)%b;
    }
    inline ll calc(ll n,ll p,ll pc)
    {
        if(n<p)return fac[n];
        now+=n/p;
        return fac[n%pc]*power(fac[pc-1],n/pc,pc)%pc*calc(n/p,p,pc)%pc;
    }
    inline ll solve(ll p,ll pc)
    {
        fac[0]=1;
        for1(i,pc-1)fac[i]=fac[i-1]*(i%p?i:1)%pc;
        now=0;
        ll t1=calc(n,p,pc),t2=1,cx=now,cy;
        for1(i,pc-1)fac[i]=fac[i-1]*(i%p?inv(i,pc):1)%pc;
        now=0;
        for1(i,m)t2=t2*calc(v[i],p,pc)%pc;
        cy=now;
        return t1*t2%pc*power(p,cx-cy,pc)%pc;
    }    
    
    int main()
    
    {
    
        freopen("input.txt","r",stdin);
    
        freopen("output.txt","w",stdout);
    
        mod=read();n=read();m=read();
        for1(i,m)v[i]=read(),sum+=v[i];
        if(sum>n){printf("Impossible
    ");return 0;}
        if(sum<n)v[++m]=n-sum;
        divide(mod);
        ll ans=0;
        for1(i,cnt)
         {
             ll t1=solve(p[i],pc[i]),t2=inv(mod/pc[i],pc[i]);
             (ans+=t1*t2%mod*(mod/pc[i])%mod)%=mod;
         }
        cout<<(ans+mod)%mod<<endl;
    
        return 0;
    
    }  

    5.n,m<=10^9  p<=10^9

       目前无解决方法貌似?当然你要是和我说 pi^ci<=10^5 我就呵呵了。。。

      

  • 相关阅读:
    Hdu 5396 Expression (区间Dp)
    Lightoj 1174
    codeforces 570 D. Tree Requests (dfs)
    codeforces 570 E. Pig and Palindromes (DP)
    Hdu 5385 The path
    Hdu 5384 Danganronpa (AC自动机模板)
    Hdu 5372 Segment Game (树状数组)
    Hdu 5379 Mahjong tree (dfs + 组合数)
    Hdu 5371 Hotaru's problem (manacher+枚举)
    Face The Right Way---hdu3276(开关问题)
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/4201833.html
Copyright © 2011-2022 走看看