验证链接:https://www.luogu.com.cn/problem/P5091
namespace exET {
int calcA(char *a, int mod) {
ll res = 0;
for(int i = 1, n = strlen(a + 1); i <= n; ++i) {
res = res * 10 + (a[i] - '0');
if(res >= mod)
res %= mod;
}
return res;
}
int calcB(char *b, int mod) {
ll res = 0, flag = 0;
for(int i = 1, n = strlen(b + 1); i <= n; ++i) {
res = res * 10 + (b[i] - '0');
if(res >= mod) {
res %= mod;
flag = 1;
}
}
if(flag == 1)
res += mod;
return res;
}
ll qpow(ll a, ll n, ll mod) {
ll res = 1;
while(n) {
if(n & 1)
res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
ll exet(char *A, char *B, int m) {
int phim = phi(m);
int a = calcA(A, m);
int b = calcB(B, phim);
int res = qpow(a, b, m);
return res;
}
}
其中,计算欧拉函数的部分如下:
const int MAXN = 1e6 + 10;
int p[MAXN], ptop;
int pm[MAXN];
void sieve(int n) {
pm[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!pm[i])
p[++ptop] = i, pm[i] = i;
for(int j = 1; j <= ptop; ++j) {
int t = i * p[j];
if(t > n)
break;
pm[t] = p[j];
if(i % p[j])
;
else
break;
}
}
}
int phi(int n) {
int res = n;
for(int i = 1; p[i]*p[i] <= n; ++i) {
if(n % p[i] == 0) {
res = res / p[i] * (p[i] - 1);
while(n % p[i] == 0)
n /= p[i];
}
}
if(n > 1)
res = res / n * (n - 1);
return res;
}
欧拉定理:若 (gcd(a,m)=1) ,则 (a^{varphi(m)}equiv1 (mod m)) 。
扩展欧拉定理:若 (gcd(a,m)
eq 1) ,当 (b<varphi(m)) 则直接求解 (a^{b}mod m)) ,当 (bgeqvarphi(m)) 则 (a^{b}equiv a^{bmod varphi(m) + varphi(m)}(mod m)) 。
注意其实是 (a^requiv a^{r+s}(mod m)) ,r是循环节的起点,s是循环节的长度,证明可以用鸽巢原理来证明。
当 (a=2,m=6) 时,观察下面的序列:
b | 0 1 2 3 4 5 6
a^b | 1 2 4 8 16 32 64
a^b mod m | 1 2 4 2 4 2 4
容易看出 (a^0 eq a^2) ,故 (a^{b} otequiv a^{bmod varphi(m) + varphi(m)}(mod m)) ,扩展欧拉定理生效必须要先超过循环的起点。
算法流程:
- 输入 (m) ,并计算 (m) 的欧拉函数 (varphi(m))
- 输入 (a) ,同时令 (a) 对 (m) 取模
- 输入 (b) ,同时令 (b) 对 (varphi(m)) 取模,若 (varphi(m)=m-1) 且最后取了至少1次模,则令 (b:=b+varphi(m))
- 快速幂计算 (a^b mod m)
算法复杂度为 (O(sqrt{m}+log{a}+log{b}))