前置概念:
1.同余式:a,b模m的余数相同,称a,b模m同余,记作a≡b(mod m).如8≡2(mod 3)
2.乘法逆元:若a,b互质,那么满足a*x≡1(mod b)的x就是a在模b条件下的乘法逆元
3.欧拉函数φ(n):表示1到n中与n互质的数的个数
4.剩余类(同余类):给定一个正整数n,可以把整数集按照模n的值分为n类,对于一个余数r,用C=nx+r来表示一个类的通式,这个通式所表示的类就称作剩余类
5.完全剩余系(完系):n个剩余类的集合,每个剩余类只取一个数来代表。如{0,1,2,3,4}是5的一个完全剩余系,{5,1,-3,8,9}也是5的一个完全剩余系
6.简化剩余系(缩系):在完系的基础上去掉对应的与n不互质的r之后剩下的系,如{1,4,5}是6的一个简化剩余系
欧拉定理及其证明:
若gcd(a,m)=1,则aφ(m) ≡ 1 (mod m)
证明:构造一个模m的简化剩余系{r1,r2,r3...rn},则{a*r1,a*r2,a*r3...a*rn}也是m的一个剩余系,
所以 ∏Ψ(m)i=1 ri ≡ ∏Ψ(m)i=1 a*ri ≡ aΨ(m) ∏Ψ(m)i=1 ri (mod m)
把∏Ψ(m)i=1 ri 约掉,就能得到aψ(m) ≡ 1(mod m)
对于费马小定理,m为质数时,φ(m)=m-1,即am-1≡1(mod m)是欧拉定理的特殊情况
对于m非质数,但gcd(a,m)=1的情况下,a的逆元可以表示为aΨ(m)-1
以下为欧拉函数的筛法及证明:
对于欧拉函数有以下性质:
1.若p为质数,φ(p)=p-1,且φ(pk)=(p-1)*pk-1
2.积性函数:若gcd(m,n)=1,则φ(mn)=φ(m)*φ(n)
对于性质1可以这样理解:将pk分为k组,只有p,p2,p3...pk与其不互质,其余均互质,因此可以这样表示。
欧拉函数的计算公式推导:由唯一分解定理n=∏si=1 piai=(p1a1p2a2p3a3p4a4...)其中p均为质数,a是对应的个数。
这里再补充一点:
(26ICPC南昌邀请L题)
那么 φ(n)= ∏si=1 φ(piai) (运用性质二,分解为多个质数的次方倍的欧拉函数值累乘)
=∏si=1 piai-1(pi-1) (运用性质一)
=∏si=1 piai(1-1/pi)
=∏si=1 piai * ∏si=1(1-1/pi)
=n* ∏si=1((pi-1)/pi) (运用唯一分解定理)
=n*(p1-1)/p1*(p2-1)/p2*...*(pn-1)/pn
由此可见,欧拉函数只与n以及质因子决定,与质因子的次数无关,可以在试除法的过程中将其递推出来。
int phi(int n){
int res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0)n/=i;
}
}
if(n>1)res=res/n*(n-1);
return res;
}
那么如何用筛法去预处理除欧拉函数呢?在质数筛中,每个合数m都是被最小的质因子筛掉的,如果p是m的最小质因子,则m通过m=p*i筛掉,在此过程中:
1.如果i能够被p整除,又由于i*p=m,所以i包含了m的所有质因数,也包括p,那么φ(m)=m*∏si=1((pi-1)/pi)=p*i*∏si=1((pi-1)/pi)=p*φ(i)
2.如果不能被p整除,i和p互质,φ(m)=φ(p)*φ(i)=(p-1)*φ(i)
比对发现,我们只需要最小质因子p和φ(i)就可以,因此可以在质数筛的过程中预处理出所有欧拉函数的值。
const int N=2e5+5;
vector<int> prime;
int vis[N],phi[N];
void init(){
phi[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
prime.push_back(i);
phi[i]=i-1;//质数单独判断
}
for(auto p:prime){
if(p*i>=N)break;
vis[p*i]=1;
if(i%p==0){//俩种情况递推即可
phi[p*i]=p*phi[i];
break;
}else phi[p*i]=phi[i]*(p-1);
}
}
}
扩展欧拉定理及其证明:

该定理可用中国剩余定理证明,但是本蒟蒻还没学(),后面会补上,所以这里就先用比较笨重的一种证明方式
要证明b>=φ(m)时的结论,我们首先证明一个引理:在此条件下,ab ≡ ab+φ(m)(mod m).这等价于证明(ab+φ(m)-ab)能够被m整除,即m | ab(aφ(m)-1)
我们构造俩个辅助数:d=gcd(aφ(m),m),并令k使得d*k=m;
后面的整体证明过程围绕d | ab以及k | aφ(m)-1俩部分组成,当证明这俩者时,合并之后就可以证明引理
1.d | ab:首先b>=φ(m)所以可以让ab=aφ(m)*ab-φ(m);由于d=gcd(aφ(m),m),因此d可以被aφ(m)整除,也就意味着ab可以被d整除。
2.k | aφ(m)-1:首先用反证法证明gcd(k,a)=1(这一步有点复杂,感兴趣的自行研究),之后我们对k和a运用欧拉定理:aφ(k)≡1(mod k),再利用欧拉函数的整除性质得到 aφ(m)=(aφ(k))t ≡ 1t ≡ 1(mod k),因此k | aφ(m)-1得证
合并结论后引理得证,之后我们通过引理推导扩展欧拉定理:利用引理的反向形式 ab ≡ ab−φ(m) (mod m),线性地删去φ(m)即可。
这里要注意一个点,因为这个式子是引理的反向形式,因此我们只有在b-φ(m)>=φ(m)的情况下才能保证正确性,这个点解释了为什么 ab ≡ ab mod φ(m)+φ(m) (mod m),而不是ab ≡ ab mod φ(m) (mod m)。
在具体应用的过程中,由于b的数字会相当大,会用到高精度,那如何用高精度去模拟取模的过程,这里还需要一种算法:秦九韶算法:
对于一个大数字可以逐位分解处理:比如1145=((1*10+1)*10+4)*10+5,由于取模在加法和乘法之间可以随意取,就可以逐位取模
洛谷模版p5091
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned long long
const int N=2e5+5;
int a,mod;string s;
int phi(int n){
int res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0)n/=i;
}
}
if(n>1)res=res/n*(n-1);
return res;
}
int depow(int phi){
int res=0;
bool ok=false;
for(int i=0;s[i];i++){
res=res*10+s[i]-'0';
if(res>=phi)ok=1,res%=phi;
}
if(ok)res+=phi;
return res;
}
int pq(int a,int b){
int res=1;
while(b){
if(b&1)res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
void solve(){
cin>>a>>mod;
cin>>s;
cout<<pq(a,depow(phi(mod)));
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int _=1;
//cin>>_;
while(_--)solve();
}
浙公网安备 33010602011771号