【数学】欧拉定理
验证链接: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) \neq 1\) ,当 \(b<\varphi(m)\) 则直接求解 \(a^{b}\mod m)\) ,当 \(b\geq\varphi(m)\) 则 \(a^{b}\equiv a^{b\mod \varphi(m) + \varphi(m)}(\mod m)\) 。
注意其实是 \(a^r\equiv 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 \neq a^2\) ,故 \(a^{b}\not\equiv a^{b\mod \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})\)