題目
設函數
$$log_a*(x) = \begin{cases}
-1, & \text{ if } x < 1 \\
1+log_a*(log_ax) & \text{ if } x \geq 1
\end{cases}$$
求最小的正整數 $x$,使得 $log_a*(x) \geq b$
分析
通過將遞歸式展開,展開 $b$ 次等於1,所以 $x$ 為 $a^{a^{a^{...}}}$(共 $b$ 次)
由歐拉降冪公式
$$a^b= \begin{cases} a^{b \% \varphi(p)} & gcd(a,p)=1 \\ a^b & gcd(a,p)\neq 1,b < \varphi (p) \\ a^{b\% \varphi (p) + \varphi (p)} & gcd(a,p)\neq 1,b \geq \varphi (p) \end{cases}$$
用這個公式,需要討論 $b$ 與 $\varphi(p)$ 的大小關系,很麻煩。
看網上的博客,有一種精妙的方法,只需重寫 Mod 函數,就能當作 $a$ 與 $p$ 互素處理。證明見 博客。
要點:
- 快速冪和cal函數都要換成Mod;
- 最終答案需要模p
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a, b, p; ll Mod(ll x, ll mod) { return x < mod ? x : x % mod + mod; } ll euler_phi(ll n) { ll m = (ll)sqrt(n + 0.5); ll ans = n; for (ll i = 2; i <= m; i++) { if (n % i == 0) { ans = ans / i * (i - 1); while (n % i == 0) n /= i; //除盡 } } if (n > 1) ans = ans / n * (n - 1); //剩下的不為1,也是素數 return ans; } ll qpow(ll a, ll b, ll p) { ll ret = 1; while(b) { if(b&1) ret = Mod(ret * a, p); a = Mod(a * a ,p); b >>= 1; } return ret; } ll cal(ll a, ll b, ll p) //a^a^a..^a共b次 { // printf("%lld %lld\n", t, p); //if(t == 1) return Mod(a, p); if(b == 0) return Mod(1, p); if(p == 1) return Mod(a, p); ll phip = euler_phi(p); return qpow(a, cal(a, b-1, phip), p); //第一類和第三類 } int main() { int T; scanf("%d", &T); while(T--) { scanf("%lld%lld%lld", &a, &b, &p); printf("%lld\n", cal(a, b, p) % p); //這個取模不能少 } return 0; }
之前寫了一種需要討論 $b$ 與 $\varphi(p)$ 大小的,
因為模數 $p \leq 1e6$,可以找找 $b$ 小於 $\varphi(p)$ 的情況
打表發現,
1 2 4 16 65536 1 3 27 97484987 739387 1 4 256 6084096 61392896 1 5 3125 8203125 8203125 1 6 46656 63878656 38438656 1 7 823543 70132343 33172343 1 8 16777216 21126656 19449856 1 9 87420489 27177289 45865289
當次數大於3時,只有 $a=2$ 可能小於 $\varphi(p)$,特判一下就好了。

#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a, b, p; int table[10] = {1, 2, 4, 16,65536}; ll qpow(ll a, ll b, ll p) { a = a%p; ll ret = 1%p; while(b) { if(b&1) ret = ret*a%p; a = a*a%p; b >>= 1; } return ret%p; } ll euler_phi(ll n) { ll m = (ll)sqrt(n + 0.5); ll ans = n; for (ll i = 2; i <= m; i++) { if (n % i == 0) { ans = ans / i * (i - 1); while (n % i == 0) n /= i; //除盡 } } if (n > 1) ans = ans / n * (n - 1); //剩下的不為1,也是素數 return ans; } ll f(ll p, ll t) { //printf("%lld %lld\n", p, t); if(t == 1) return a%p; if(t == 2) return qpow(a, a, p); if(t == 0) return 1%p; if(p == 1) return 0; ll phip = euler_phi(p); if(p % a == 0) //t >= 3,若出現第二種情況,a只能為2 { if(a == 2 && t <= 4 && table[t-1] < phip) //return qpow(a, f(p, t-1), p); return table[t]%p; } return qpow(a, f(phip, t-1)+phip, p); //第一類和第三類 } int main() { int T; scanf("%d", &T); while(T--) { scanf("%lld%lld%lld", &a, &b, &p); printf("%lld\n", f(p, b)); } return 0; }
參考鏈接:https://blog.csdn.net/qq_35914587/article/details/79883547