近期看到一类很有趣的题啊,其最基础的表现形式为求 mod P的值。
所以我们来拿一道小例题讲讲。
题面:给定 x,n,m,求:
mod 1000003471的值。
首先我们注意到,题目给定的模数1000003471为质数,根据费马小定理,≡
(mod P)。则只需求出
mod (P-1) 即可。
接下来我们将P-1做质因数拆分,得到其质因子{2,3,5,53,677,929},那么我们可以套用中国剩余定理,将 mod (P-1) ≡ y拆解成数个形似
mod pi ≡ yi的方程,结合中国剩余定理便可得到原始方程。
在求yi的过程中,我们注意到其模数极小,所以无法套用阶乘与逆元的求组合数,我们需要使用到Lucas定理,其针对的便是求出组合数对于小质数取模的结果。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int mod=1000003471;
inline ll qpow(ll x,ll y,int p){
ll r=1;
while(y){
if(y&1) r=r*x%p;
x=x*x%p;
y>>=1;
}
return r;
}
ll inv[maxn],fac[maxn];
inline ll c(int n,int m,int p){
if(n<m) return 0;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
ll lucas(int n,int m,int p){
if(!m) return 1;
return lucas(n/p,m/p,p)*c(n%p,m%p,p)%p;
}
inline ll getl(int n,int m,int p){
fac[0]=1;
for(int i=1;i<p;i++) fac[i]=fac[i-1]*i%p;
inv[p-1]=qpow(fac[p-1],p-2,p);
for(int i=p-2;~i;i--) inv[i]=inv[i+1]*(i+1)%p;
return lucas(n,m,p);
}
ll X,Y,G,lcm;
void exgcd(int a,int b){
if(!b){
X=1,Y=0,G=a;
return;
}
exgcd(b,a%b);
ll t=X;
X=Y;
Y=t-a/b*X;
}
void getxy(ll a,ll b,int c){
exgcd(a,b);
X*=c/G;
lcm=a/G*b;
b/=G;
X=(X%b+b)%b;
}
pair<ll,ll> merge(ll a1,ll n1,ll a2,ll n2){
getxy(n1,n2,a2-a1);
return {(X*n1%lcm+a1)%lcm,lcm};
}
int a[7],b[7]={0,2,3,5,53,677,929},n,m,tot,x;
ll excrt(){
pair<ll,ll> cur={0,1};
for(int i=1;i<=tot;i++) cur=merge(cur.first,cur.second,a[i],b[i]);
return cur.first;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
tot=6;
cin>>x>>n>>m;
for(int i=1;i<=tot;i++) a[i]=getl(n,m,b[i]);
ll q=excrt();
cout<<qpow(x,q,mod)<<endl;
return 0;
}
接下来我们再看一道复杂一点的
P2480 [SDOI2010] 古代猪文
同样,简化题面,即为求出 mod 999911659。
同样是套用上述方法,拆解出模数质因数为{2,3,4679,35617}。
在求对应yi是对于每个n的因数套用Lucas定理即可,注意特判。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int mod=999911659;
const int b[]={0,2,3,4679,35617};
inline ll qpow(ll x,int y,int p){
x%=mod;
ll r=1;
while(y){
if(y&1) r=r*x%p;
x=x*x%p;
y>>=1;
}
return r;
}
int n,g,m=4;
ll fac[maxn],inv[maxn],a[5];
inline ll c(int n,int m,int p){
if(n<m) return 0;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
ll lucas(int n,int m,int p){
if(!m) return 1;
return lucas(n/p,m/p,p)*c(n%p,m%p,p)%p;
}
void so(int p,ll &ans){
fac[0]=1;
for(int i=1;i<p;i++) fac[i]=fac[i-1]*i%p;
inv[p-1]=qpow(fac[p-1],p-2,p);
for(int i=p-2;~i;i--) inv[i]=inv[i+1]*(i+1)%p;
for(ll i=1;i*i<=n;i++){
if(n%i==0){
ans+=lucas(n,i,p);
if(n/i!=i) ans+=lucas(n,n/i,p);
ans%=p;
}
}
}
ll X,Y,G,lcm;
void exgcd(int a,int b){
if(!b){
X=1,Y=0,G=a;
return;
}
exgcd(b,a%b);
ll t=X;
X=Y;
Y=t-a/b*Y;
lcm=(ll)a/G*b;
}
void getxy(int a,int b,int c){
exgcd(a,b);
X*=c/G;
b/=G;
X=(X%b+b)%b;
}
pair<ll,ll> merge(int a1,int n1,int a2,int n2){
getxy(n1,n2,a2-a1);
return {(X*n1%lcm+a1)%lcm,lcm};
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>g;
if(g%mod==0){
cout<<0<<endl;
return 0;
}
for(int i=1;i<=m;i++) so(b[i],a[i]);
pair<ll,ll> cur={0,1};
for(int i=1;i<=m;i++) cur=merge(cur.first,cur.second,a[i],b[i]);
cout<<qpow(g,cur.first,mod)<<endl;
return 0;
}
/*
2
3
4679
35617
*/