前置概念:

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 r约掉,就能得到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是对应的个数。

这里再补充一点:image(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);
        }
    }
}

 

扩展欧拉定理及其证明:

image

该定理可用中国剩余定理证明,但是本蒟蒻还没学(),后面会补上,所以这里就先用比较笨重的一种证明方式

要证明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();
}

 

posted on 2026-07-19 14:46  LeoCodex  阅读(4)  评论(0)    收藏  举报