大组合数取模

考虑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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值