PowMod (欧拉推式子 + 指数循环节)

最主要的步骤是用 1式子和2式子推 3式子。(难点,看了很多博客最后的时候那个式子看不懂)

  1. 当n, m互质时即gcd(n, m) == 1,存在phi(n * m) = phi(m) * phi(n)
  2. 当m为素数且n%m == 0时,存在phi(n*m) = phi(n) * m
  3. 记  为S(n, m),存在S(n,m) = S(n/p, m) * (p – 1) + S(n, m/p) (其中p为素数)
#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int MAXN = 1e7 + 9;
const int mod  = 1e9 + 7;
bool check[MAXN];
int phi[MAXN], prime[MAXN], tot;
LL sum[MAXN],now[MAXN];

LL myPow(LL a, int p, LL mod){
    LL ret = 1;
    while(p){
        if(p & 1) ret = ret * a % mod;
        a = a * a % mod;
        p >>= 1;
    }
    return ret;
}

void phi_and_prime_table(int N) {
    memset(check,false,sizeof(check));
    phi[1] = 1;
    tot = 0;
    for(int i = 2; i <= N; i++) {
        if( !check[i] ) {
            prime[tot++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; j < tot; j++) {
            if(i * prime[j] > N)
                break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else {
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            }
        }
    }
    for(int i = 1; i <= N; i ++){
        sum[i] =  (sum[i - 1] + phi[i]) % mod;
    }
}

LL S(int n, int m){
    if( n == 1 ) return sum[m];
    if( m == 0 ) return 0;
    for(int i = 0; i < tot && prime[i] <= n; i ++){
        if( n % prime[i] == 0 ) {
            int p = prime[i];
            return (( 1LL * (p - 1) * S(n/p, m))%mod + S(n, m/p) % mod ) % mod;
        }
    }
}

LL A(int k,int p){
    if(k == 1) return 1;
    if(p == 1) return 0;
    return myPow(k, phi[p] + A(k, phi[p]), p);
}

int main() {
    phi_and_prime_table(10000000);
    int n,m,p;
    while(~scanf("%d%d%d",&n,&m,&p)){
        int k = S(n,m);
        printf("%lld\n",A(k, p));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wethura/p/9727300.html