1. 程式人生 > >大組合數取模

大組合數取模

考慮C(n,m)%P
情況一:n,m很大,P為素數
處理小範圍的階乘和階乘的逆元 用盧卡斯定理即可。盧卡斯定理:
這裡寫圖片描述

情況二: 當P=

p1p2p3...pn
求出[Cmn]分別在[p1,p2,p3,,pn]模意義下的結果,記為 [m1,m2,m3,,mn]

得到模方程組

ximi(modpi)1in用中國剩餘定理求解即可。
程式碼如下:
#include<cstdio>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<algorithm> #include<cmath> #define LL long long const LL M= 999911659; const int maxn= 50000+5; using namespace std; template <typename T> inline void _read(T &x){ char ch=getchar(); bool mark=false; for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true; for(x=0
;isdigit(ch);ch=getchar())x=x*10+ch-'0'; if(mark)x=-x; } LL P[4]= {2,3,4679,35617}; LL N,G,fra[maxn][4],inv[maxn][4]; LL ans[4]; LL pow_mod(LL x,LL y,LL M){ LL ans=1; for(x%=M;y;y>>=1,x=x*x%M) if(y&1) ans=ans*x%M; return ans; } // 解模方程組 x=A[i] (moc m[i]) 0<=i<n
void EXGCD(LL A,LL B,LL& d,LL& x,LL& y){ if(B==0){d=A;x=1;y=0;} else {EXGCD(B,A%B,d,y,x); y-= x*(A/B);} } LL China(LL * A,LL * m,LL n){ LL M= 1,ret=0,i; for(i=0;i<n;i++)M*= m[i]; for(i=0;i<n;i++){ LL d,x,y,t= M/m[i]; EXGCD(m[i],t,d,x,y); ret= (ret+ A[i]*y*t%M)%M; } return (ret+M)%M; } LL C(LL n,LL m,LL id){ if(m>n) return 0; if(!m||n==m) return 1; if(n<=P[id] && m<=P[id]) return fra[n][id]*inv[m][id]%P[id]*inv[n-m][id]%P[id]; return C(n/P[id],m/P[id],id)*C(n%P[id],m%P[id],id)%P[id]; } void Divide(LL n){ LL i,j; for(i=1;i*i<=n;i++){ if(n%i!=0) continue; for(j=0;j<4;j++){ ans[j]= ans[j]+C(n,i,j); if(ans[j]>=P[j]) ans[j]-=P[j]; } if(i*i!=n){ for(j=0;j<4;j++){ ans[j]= ans[j]+ C(n,n/i,j); if(ans[j]>=P[j]) ans[j]-=P[j]; } } } } void Init(){ LL i,j; for(i=0;i<4;i++){ //分別預處理每一個質數 LL mod= P[i]; inv[1][i]= fra[1][i]=1; inv[0][i]= fra[0][i]=1; for(j=2;j<=P[i];j++){ fra[j][i]= fra[j-1][i]*j%mod; inv[j][i]= (mod-mod/j)*inv[mod%j][i]%mod; } for(j=2;j<=P[i];j++) inv[j][i]= inv[j][i]*inv[j-1][i]%mod; } } int main(){ cin>>N>>G; Init(); Divide(N); //for(int i=0;i<4;i++) cout<<ans[i]<<" ";cout<<endl; LL exp= China(ans,P,4); if(exp==0) exp= M-1; cout<<pow_mod(G,exp,M)<<endl; return 0; } /* 900951975 90523153 81793986 */

終極情況:C(n,m)%p
n,m非常大 -> 盧卡斯定理
p=p1^c1 * p2^c2 * … * pn^cn
由之前的情況我們已經可以得到了一些結論,這種情況怎麼辦?
同理 盧卡斯定理 + CRT
但是前提條件是要求 C(n,m)% pi^ci
C(n,m)% pi^ci怎麼求?
這裡寫圖片描述

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define ll long long
using namespace std;
const ll label=1000000000000000LL;
template <typename T>
inline void _read(T& x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
ll mont(ll x,ll y,ll z){
    ll temp=1;
    for(x%=z;y;y>>=1,x=x*x%z){
        if(y&1)temp=temp*x%z;
    }
    return temp;
}
ll exgcd(ll a,ll b,ll &x,ll &y){  
    if(b==0){  
        x=1;y=0;return a;  
    }
    else{  
        ll ans=exgcd(b,a%b,x,y);  
        ll t=x;  
        x=y;y=t-a/b*y;return ans;  
    }
}
ll rev(ll a,ll b){
    ll x,y;
    exgcd(a,b,x,y);
    return (x%b+b)%b;
}
ll C(ll n,ll m,ll mod){
    if(m>n)return 0;
    ll ans=1,i,a,b;
    for(i=1;i<=m;i++){
        a=(n+1-i)%mod;
        b=rev(i%mod,mod);
        ans=ans*a%mod*b%mod;
    }
    return ans;
}
ll C1(ll n,ll m,ll mod){
    if(m==0)return 1;
    return C(n%mod,m%mod,mod)*C1(n/mod,m/mod,mod)%mod;
}
ll cal(ll n,ll p,ll t){
    if(!n)return 1;
    ll x=mont(p,t,label),i,y=n/x,temp=1;
    for(i=1;i<=x;i++){
        if(i%p)temp=temp*i%x;
    }
    ll ans=mont(temp,y,x);
    for(i=y*x+1;i<=n;i++){
        if(i%p)ans=ans*i%x;
    }
    return ans*cal(n/p,p,t)%x;
}
ll C2(ll n,ll m,ll p,ll t){
    ll x=mont(p,t,label);
    ll a,b,c,ap=0,bp=0,cp=0,temp;
    for(temp=n;temp;temp/=p)ap+=temp/p;
    for(temp=m;temp;temp/=p)bp+=temp/p;
    for(temp=n-m;temp;temp/=p)cp+=temp/p;
    ap=ap-bp-cp;
    ll ans=mont(p,ap,x);
    a=cal(n,p,t);
    b=cal(m,p,t);
    c=cal(n-m,p,t);
    ans=ans*a%x*rev(b,x)%x*rev(c,x)%x;
    return ans;
}
ll CRT(ll A[],ll B[],ll r){
    ll M=1;
    ll i,j,k,ans=0,d,t,x0,y0;
    for(i=1;i<=r;i++){
        M*=A[i];
    }
    for(i=1;i<=r;i++){
        d=M/A[i];
        exgcd(d,A[i],x0,y0);
        ans=(ans+d*x0*B[i])%M;
    }
    while(ans<=0)ans+=M;
    return ans;
}
ll lucas(ll x,ll y,ll mod){
    ll i,j,k,d,cnt=0,t;
    ll A[205],M[205];
    for(i=2;i*i<=mod;i++){
        if(mod%i==0){
            t=0;
            while(mod%i==0){
                mod/=i;
                t++;
            }
            M[++cnt]=mont(i,t,label);
            if(t==1)A[cnt]=C1(x,y,i);
            else A[cnt]=C2(x,y,i,t);
        }
    }
    if(mod>1){
        M[++cnt]=mod;
        A[cnt]=C1(x,y,mod);
    }
    /*cout<<"divide:"<<endl;
    for(i=1;i<=cnt;i++){
        cout<<"M:"<<M[i]<<" residue:"<<A[i]<<endl;
    }*/
    return CRT(M,A,cnt);
}
int T;
int main(){
    ll x,y,temp;
    cin>>T;
    while(T--){
        scanf("%I64d%I64d%I64d",&x,&y,&temp);
        printf("%I64d\n",lucas(x,y,temp));
    }
}
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const ll label=1000000000000000LL;
template <typename T>
inline void _read(T& x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int T;
ll mod;
ll cnt;
ll n,m,n1,n2,c;
ll a[25];
ll A[205],M[205],prime[205],index[205];
ll F[50005];

ll mont(ll x,ll y,ll z){
    ll temp=1;
    for(x%=z;y;y>>=1,x=1ll*x*x%z){
        if(y&1)temp=1ll*temp*x%z;
    }
    return temp;
}
void pre(){
    ll MOD=mod,i,t;
    for(i=2;i*i<=MOD;i++){
        if(MOD%i==0){
            t=0;
            prime[++cnt]=i;
            while(MOD%i==0){
                MOD/=i;
                t++;
            }
            M[cnt]=mont(i,t,label);
            index[cnt]=t;
        }
    }
    if(MOD>1){
        M[++cnt]=MOD;
        prime[cnt]=MOD;
        index[cnt]=1;
    }
}
ll exgcd(ll a,ll b,ll &x,ll &y){  
    if(b==0){  
        x=1;y=0;return a;  
    }
    else{  
        ll ans=exgcd(b,a%b,x,y);  
        ll t=x;  
        x=y;y=t-a/b*y;return ans;  
    }
}

ll _fac(ll n,ll t){
    if(n<prime[t])return F[n];
    c+=n/prime[t];
    return 1ll*mont(F[M[t]-1],n/M[t],M[t])*F[n%M[t]]%M[t]*_fac(n/prime[t],t)%M[t];
}
ll C(ll n,ll m,ll t){
    if(n<m)return 0;
    if(m<0)return 0;
    if(n==m)return 1;
    ll i,p,temp,x,y,s;
    F[0]=1;
    for(i=1;i<M[t];i++){
        if(i%prime[t])F[i]=F[i-1]*i%M[t];
        else F[i]=F[i-1];
    }
    c=0;
    p=_fac(n,t);
    s=c;c=0;
    temp=1ll*_fac(m,t)*_fac(n-m,t)%M[t];
    exgcd(temp,M[t],x,y);
    x=(x+M[t])%M[t];
    p=1ll*p*x%M[t]*mont(prime[t],s-c,M[t])%M[t];
    return p;
}
ll lucas(ll n,ll m){
    if(n<m||m<0)return 0;
    if(n==m)return 1;

    //cout<<"lucas("<<n<<","<<m<<")"<<endl;
    ll temp=0,x,y,i,t;
    for(i=1;i<=cnt;i++){
        //cout<<"i:"<<i<<" prime:"<<prime[i]<<" mod:"<<M[i]<<" index:"<<index[i]<<endl;
        t=mod/M[i];
        exgcd(t,M[i],x,y);x=(x+M[i])%M[i];
        temp=(temp+1ll*x*C(n,m,i)%mod*t%mod)%mod;
    }
    return temp;
}
int main(){
    ll i,j,k;
    cin>>mod;
    pre();
}

相關推薦

Lucas定理應用分析——合數

    首先給出Lucas(盧卡斯)定理:     有非負整數A、B,和素數p,A、B寫成p進製為:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。 則組合數C(A,B)與C(a[n],b[n])×C(a[n-1],b[n-1])×...×C

合數-盧卡斯定理

求左邊的     為: 通過觀察你會發現當且僅當i = t , j = r ,能夠得到的係數,及。 所以,。得證。 -------------------------------------------------------------------------------------------

Lucas定理 合數

對於C(n, m) mod p。這裡的n,m,p(p為素數)都很大的情況。就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式遞推了。 這裡用到Lusac定理 For non-negative integers m and n and a prime p, the f

合數

考慮C(n,m)%P 情況一:n,m很大,P為素數 處理小範圍的階乘和階乘的逆元 用盧卡斯定理即可。盧卡斯定理: 情況二: 當P= p1∗p2∗p3∗...∗pn 求出[Cmn]分別在[p1,p2,p3,…,pn]模意義下的結果,記為 [m1,m

Lucas定理——合數

大組合數取模,求C[n][m]%p 公式:C[n][m]%p == C[n%p][m%p]*C[n/p][m/p]%p 注意,Lucas的要求是n,m<=10^5,如果n,m>=10^5,那麼要求p<=10^5 楊輝三角: f[0][

Lucas定理及合數

引入 楊輝三角 std 數據 組合數取模 有關 ans main include 引入: 組合數C(m,n)表示在m個不同的元素中取出n個元素(不要求有序),產生的方案數。定義式:C(m,n)=m!/(n!*(m-n)!)(並不會使用LaTex QAQ)。 根據題目中對組合

合數

ios AS names 局限性 代碼 lap div 沒有 AC 組合數取模問題為求$C_{n}^m % p$的值。根據$n$,$m$,$p$取值不同,方法不同。在此之前我們先看些前置技能: 同余定理:$a≡b(mod\ m)$性質:1.傳遞性:若$a≡b(mod\

合數&&Lucas定理題集

pac 假設 次方 href ace 範圍 統一 lucas定理 != 題集鏈接: https://cn.vjudge.net/contest/231988 解題之前請先了解組合數取模和Lucas定理 A : FZU-2020 輸出組合數C(n, m) mod p (

Uva12034 (合數

傳遞 組合數取模 組合 並且 gen mod 總數 比賽結果 對組 題意:兩匹馬比賽有三種比賽結果,n匹馬比賽的所有可能結果總數 解法: 設答案是f[n],則假設第一名有i個人,有C(n,i)種可能,接下來還有f(n-i)種可能性,因此答案為 ΣC(n,i)f(n-i) 另

合數1:盧卡斯定理

模板: #include<iostream> #include<algorithm> #include<cstdio> #define ll long long #define N 100005 using namespace std; int k,n,m

求組合數以及合數

1、採用C(a, b) = n! / (m! * (n - m)!),適用範圍為n <= 20 typedef long long ll; const int maxn=20+5; ll a[maxn]; void init() { a[0]=1; for(int i=1; i&l

[學習筆記]合數的幾種求法

一、引入 給定 n n n ,

[演算法 18_001] Lucas 定理與合數

Lucas 定理 該定理是用來求當 (nm) ( n m

2018 Wannafly summer camp Day3-- Travel (思維 合數)

題目大意:        魔方國有n座城市,編號為。城市之間通過n-1條無向道路連線,形成一個樹形結構。瀾瀾打算在魔方國進行m次旅遊,每次遊覽至少一座城市。為了方便,每次旅遊遊覽的城市必須是連通

合數(逆元+快速冪)

組合大發好 一般我們用楊輝三角性質 楊輝三角上的每一個數字都等於它的左上方和右上方的和(除了邊界) 第n行,第m個就是,就是C(n, m) (從0開始) 電腦上我們就開一個數組儲存,像這樣 #include<cstdio> const int

各種逆元求法 合數 comb (合數 Lucas)

組合數取模(comb) 【問題描述】 計算C(m,n)mod 9901的值 【輸入格式】 從檔案comb.in中輸入資料。 輸入的第一行包含兩個整數,m和n 【輸出格式】 輸出到檔案comb.out中。 輸出一行,一個整數 【樣例輸入】 2

Codeforces 521C 合數(乘法逆元)

【解題報告】 之前很少遇到組合數取模的問題(做題太少了),所以就GG了……組合數取模這一問題在演算法競賽中還是很常見的,必須紮實掌握。 回到這道題目來,你需要在n個數之間放k個加號,然後求出所有方案的和。 我們知道正向思維,即求出所有的方案,然後對每個

大數合數(逆元+打表)

將階乘O(n)打表之後C(n,m)便可O(1)求出,除法取模用逆元解決 hdu5698瞬間移動 #include<bits/stdc++.h> using namespace std

合數(楊輝三角打表 & 求逆元(擴充套件歐幾里得、費馬小定理、尤拉定理、線性求法) & Lucas)

    在acm競賽中,組合數取模的題目還是經常會見到的,所以這是有必要掌握的一個演算法。我本人就因為這個東西而被坑了很多次了= =之前的部落格也都扯過了,就不多說了,下面進入正題。 (1)楊輝三角求組合數     楊輝三角這個東西應該都不陌生,三角的兩邊始終為一,之後向

求解合數---拓展歐幾里德和費馬小定理求解逆元

組合數:C(n, m) ;         組合數取模:C(n, m) % mod,mod是一個很大的數。1.公式:2.性質:(1)C(n,m)= C(n,n-m)   其中有C(n, 0) = 1;(2)C(n,m)=C(n-1,m-1)+C(n-1,m)。可以用作遞迴中的