拓展欧拉定理与幂塔函数
欧拉定理: 若gcd(a,p) = 1,则
a
p
≡
a
b
%
φ
(
p
)
(
m
o
d
 
p
)
a^p \equiv a^{b \% \varphi (p)}(mod \: p)
ap≡ab%φ(p)(modp),其中gcd(a,p) = 1。
拓展欧拉定理: 假设a为任意数,b和m为正整数,且
b
>
φ
(
m
)
b > \varphi(m)
b>φ(m),a和m不一定要互质,那么有如下公式:
a
b
≡
{
a
b
M
o
d
ϕ
(
m
)
gcd
(
a
,
m
)
=
1
a
b
gcd
(
a
,
m
)
≠
1
∧
b
<
ϕ
(
m
)
a
b
M
o
d
ϕ
(
m
)
+
ϕ
(
m
)
gcd
(
a
,
m
)
≠
1
∧
b
≥
ϕ
(
m
)
  
(
M
o
d
 
m
)
a^b~\equiv~\begin{cases}a^{b~Mod~\phi(m)} &\gcd(a,m)~=~1 \\a^b &\gcd(a,m)~\neq~1~\land~b~<~\phi(m) \\ a^{b~Mod~\phi(m)~+~\phi(m)} &\gcd(a,m)~\neq~1~\land~b~\geq~\phi(m)\end{cases} \:\:(Mod\: m)
ab ≡ ⎩⎪⎨⎪⎧ab Mod ϕ(m)abab Mod ϕ(m) + ϕ(m)gcd(a,m) = 1gcd(a,m) ̸= 1 ∧ b < ϕ(m)gcd(a,m) ̸= 1 ∧ b ≥ ϕ(m)(Modm)
注:拓展欧拉定理的证明可以参考证明
幂塔函数: 形如
a
a
a
.
.
.
m
o
d
 
m
a^{a^{a^{...}}} mod \: m
aaa...modm
的函数我们称为幂塔函数,该函数应从上向下计算,即
a
a
a
a
=
a
(
a
(
a
a
)
)
a^{a^{a^a}} = a^{(a^{({a^a})})}
aaaa=a(a(aa)),因此其中间变量会急剧上升,普通方法无法计算。
例题:2019南京icpc网络赛 B.Super_Log
测试地址
题意简述:
给定a和k以及m,试求出
a
a
a
.
.
.
m
o
d
 
m
a^{a^{a^{...}}} mod \: m
aaa...modm
其中共k层幂塔。其中a,k和m小于等于1e6。
解题思路:
首先使用拓展欧拉定理需要
b
>
φ
(
m
)
b > \varphi(m)
b>φ(m),所以对于不同的b要分类讨论一下。设
f
(
a
,
k
,
m
)
=
a
a
a
.
.
.
%
m
f(a,k,m) = a^{a^{a^{...}}} \% m
f(a,k,m)=aaa...%m
其中a是有k层的幂塔。
- 首先,当a = 1或者b = 0时特判,得出答案为 1%m。
- 本题中b = f(a, k-1,INF),如果a >= phi(m),那么显然b一定大于phi(m),即满足拓展欧拉定理中第3种情况;
- 如果k = 1,则b = f(a,k-1,INF) = 1,此时只需判断phi(m)是否大于1即可判断当然情况符合拓展欧拉定理第3还是第2种情况。
- 剩下的情况我们就可以通过递归来判断b是否大于phi(m),因为若b >= phi(m),那么 l o g a b > = l o g a p h i ( m ) log_ab >= log_a phi(m) logab>=logaphi(m)
代码示例:
#include<cstdio>
#include<cmath>
typedef long long ll;
const int N = 1e6+10;
ll qpow(ll a,ll b,ll m){
ll res = 1;
while(b){
if(b&1) res = res*a%m;
a = a*a%m;
b >>= 1;
}
return res;
}
int v[N],primes[N],phi[N];
int init(){
int cnt = 0;
for(int i = 2;i < N;i++){
if(!v[i]){
primes[cnt++] = i;
v[i] = i;
phi[i] = i-1;
}
for(int j = 0;j < cnt;j++){
if(primes[j] > v[i] || primes[j]*i >= N)
break;
v[i*primes[j]] = primes[j];
phi[i*primes[j]] = phi[i]*(i%primes[j] ? primes[j]-1 : primes[j]);
}
}
}
ll gcd(ll a,ll b){
if(b == 0) return a;
return gcd(b,a%b);
}
bool check(ll a,ll b,ll p){
if(b == 0) return p <= 1; //f(a,0,p) = 1
if(a >= p) return true;//f(a,b,INF) > p
return check(a,b-1,log(p)/log(a));
}
ll f(ll a,ll b,ll m){
if(m == 1) return 0; //递归终止条件1,此时之后答案恒为0
if(b <= 1) return qpow(a,b,m);//递归终止条件2
ll ph = phi[m];
// printf("%lld\n",ph);
if(gcd(a,m) == 1) return qpow(a,f(a,b-1,ph),m); //欧拉定理
if(check(a,b-1,ph)) return qpow(a,f(a,b-1,ph)+ph,m);//拓展欧拉定理情况1
return qpow(a,f(a,b-1,ph),m); //拓展欧拉定理情况2
}
ll a,b,m;
int t;
int main(){
scanf("%d",&t);
init();
while(t--){
scanf("%lld%lld%lld",&a,&b,&m);
printf("%lld\n",f(a,b,m));
}
return 0;
}
参考资料
- SovietPower的博客
- 《算法竞赛进阶指南》,李煜东,P142-143.