《算法竞赛进阶指南》0x36 古代猪文 Lucas定理+费马小定理+欧拉定理推论+中国剩余定理

题目链接:https://www.acwing.com/problem/content/215/

 通过欧拉定理推论可以知道这个公式的计算可以变成对指数%(mod-1)的计算,涉及到组合数取模的问题,遂考虑卢卡斯定理,由于模数不是质数,考虑分解质因数,发现是一个square free number,可以通过模线性方程组来求解这个最小的指数x,求最小的正整数x是因为,如果指数是k*(mod-1)+x的话,根据费马小定理,q^(k*(mod-1))=1%(mod),对结果没有贡献。

求解过程用到了Lucas定理,和对不同的模质数进行取模。逆元可以用exgcd或者费马小定理在O(logn)时间内求解。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod=999911659;
ll p[5]={0,2,3,4679,35617},m[5]={0},t[5];
ll pn[5][35620][2];//0存放对应的阶乘取模,1存放阶乘的逆元 
ll ksm(ll a,ll b,ll mod){
    ll ans=1;
    a%=mod;
    b%=(mod-1);//费马小定理 
    while(b){
        if(b&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
ll C(ll n,ll d,ll i){//lucas定理求组合数取模 ,使用第i个模数 
    if(d<p[i] && n<p[i])return pn[i][n][0]*pn[i][d][1]%p[i]*pn[i][n-d][1]%p[i];
    return C(n%p[i],d%p[i],i)*C(n/p[i],d/p[i],i)%p[i];
}
int main(){
    ll n,q;
    cin>>n>>q;
    if(q==mod){
        cout<<"0"<<endl;
        return 0;
    }
    for(int i=1;i<=4;i++)pn[i][0][0]=pn[i][0][1]=pn[i][1][0]=pn[i][1][1]=1;
    for(int i=2;i<=4;i++){
        for(int j=2;j<p[i];j++){
            pn[i][j][0]=pn[i][j-1][0]*j%p[i];//阶乘对应取模
            pn[i][j][1]=ksm(pn[i][j][0],p[i]-2,p[i]); 
        }
    }
    
    for(int i=1;i<=4;i++){
        m[i]=(mod-1)/p[i];
        t[i]=ksm(m[i],p[i]-2,p[i]);
    }
    ll a[5]={0,0,0,0,0};
    for(int d=1;d*d<=n;d++){//分解质因数 
        if(n%d==0){
            for(int i=1;i<=4;i++){
                a[i]=(a[i]+C(n,d,i))%p[i];
                if(d*d!=n)a[i]=(a[i]+C(n,n/d,i))%p[i];
            }
        }
    }
    ll ans=0;
    for(int i=1;i<=4;i++){//中国剩余定理 
        ans=(m[i]*a[i]%(mod-1)*t[i]%(mod-1)+ans)%(mod-1);
    }
    ans=(ans+mod-1)%(mod-1);//求得指数 
    cout<<ksm(q,ans,mod)<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/randy-lo/p/13281001.html