同余类

给定一个正整数 $n$ ,将所有整数依据模 $n$ 的余数 $r\in[0,n-1]$ 分为 $n$ 类,其中每一类都可以被表示为 $C_r=nx+r$ ,一个 $C_r$ 构成的一个集合是模 $n$ 的一个同余类

完全剩余系

给定一个正整数 $n$ ,有 $n$ 个不同的模 $n$ 的同余类,从这 $n$ 个同余类中各选出一个元素共计 $n$ 个数,这些数构成的一个集合为模 $n$ 的一个完全剩余系。显然对于正整数 $n$ 一定存在一个完全剩余系是 $Z_n=[0,n-1]$ ,该剩余系为简单完全剩余系。同理,对剩余系中任意元素加上 $n$ 的整数倍后得到的集合仍然为模 $n$ 的一个完全剩余系

简化剩余系

给定一个正整数 $n$ ,有 $\varphi(n)$ 个不同的模 $n$ 的余数 $r$ 与 $n$ 互质的同余类,在这 $\varphi(n)$ 个同余类中各选出一个元素共计 $\varphi(n)$ 个数,这些数构成的一个集合是模 $n$ 的一个简化剩余系。其中 $\varphi(n)$ 为 $n$ 的欧拉函数

$$ \varphi(n)=n\times \prod_{i=1}^s \frac{p_i-1}{p_i} $$

欧拉定理

若 ${\rm{gcd}}(a,m)=1$,则 $a^{\varphi(m)} \equiv 1({\rm{mod}}\ m)$

简单证明:

设 $\{r_1,r_2,\cdots ,r_{\varphi(m)} \}$ 是一个模 $m$ 的简化剩余系,则 $\{ar_1,ar_2,\cdots,ar_{\varphi(m)} \}$ 也是模 $m$ 的一个简化剩余系,所以有

$$ \prod_{i=1}^{\varphi(m)} r_i\equiv \prod_{i=1}^{\varphi(m)} ar_i \equiv a^{\varphi(m)}\prod_{i=1}^{\varphi(m)} r_i ({\rm{mod }}\ m) $$

同余式两侧约去 $\prod_{i=1}^{\varphi(m)} r_i$ 后得

$$ a^{\varphi(m)} \equiv 1({\rm{mod}}\ m) $$

当 $m$ 为质数时 $\varphi(m)=m-1$ ,得到费马小定理 $a^{m-1}\equiv 1({\rm{mod}}\ m)$

扩展欧拉定理

$$ a^b=\left\{ \begin{array}{l} a^{b\ {\rm{mod}}\ \varphi(m)} & {\rm{gcd}}(a,m)=1 &({\rm{mod}}\ m) \\ a^b&b<\varphi(m),{\rm{gcd}}(a,m)\ne1 &({\rm{mod}}\ m) \\ a^{b\ {\rm{mod}}\ \varphi(m)+\varphi(m)} &b\ge\varphi(m),{\rm{gcd}}(a,m)\ne1&({\rm{mod}}\ m) \end{array} \right. $$

计算 $a^b$ 时,若 $b$ 较小,直接跑一遍快速幂即可,否则先用扩展欧拉定理进行降幂再跑快速幂

对于单独的 $b$ 来说,利用试除法比筛法更加优秀

LL get_phi(LL n){
    LL res=n;
    for (LL i=2;i*i<=n;i++){
        if (n%i==0){
            res=res*(i-1)/i;
            while (n%i==0)
                n/=i;
        }
    }
    if (n>1) res=res*(n-1)/n;
    return res;
}

对 $b$ 进行降幂操作,因为 $a^{\varphi(m)} \equiv 1\to a^{b\ {\rm{mod}}\ \varphi(m)}\equiv a^{b\ {\rm{mod}}\ \varphi(m)+\varphi(m)}({\rm{mod}}\ m)$ ,所以不需要单独判断

LL depow(LL phi,string b){
    LL res=0;
    bool f=0;//判断b和phi_m的大小关系
    for (auto c:b){
        res=res*10+(c-'0');
        if (res>=phi) f=1,res%=phi;
    }
    if (f) res+=phi;//如果b>phi_m则需要加上phi_m
    return res;
}

以洛谷P5091为例:https://www.luogu.com.cn/problem/P5091

LL ksm(LL a,LL b,LL p){
    LL res=1;
    while (b){
        if (b&1) res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}

LL get_phi(LL n){
    LL res=n;
    for (LL i=2;i*i<=n;i++){
        if (n%i==0){
            res=res*(i-1)/i;
            while (n%i==0)
                n/=i;
        }
    }
    if (n>1) res=res*(n-1)/n;
    return res;
}

LL depow(LL phi,string b){
    LL res=0;
    bool f=0;
    for (auto c:b){
        res=res*10+(c-'0');
        if (res>=phi) f=1,res%=phi;
    }
    if (f) res+=phi;
    return res;
}

void solve(){
    LL a,m;
    string b;
    cin>>a>>m>>b;
    cout<<ksm(a,depow(get_phi(m),b),m)<<endl;
}