P1405 苦恼的小明 题解

· · 题解

P1405 苦恼的小明 题解

题目传送门

题意简述

给定长度为 n 的自然数序列 a,求 a_1^{a_2^{\dots^{a_n}}} \bmod 10007。

## 前置知识 [扩展欧拉定理](https://www.luogu.com.cn/problem/P5091): $$a^b \equiv \begin{cases} a^b & b < \varphi(m) \\ a^{b \bmod \varphi(m) + \varphi(m)} & b \ge \varphi(m) \end{cases} \pmod m$$ ## 解法 有了扩展欧拉定理,我们自然会有一个思路: 设 $f(x)$ 为后 $x$ 项形成的幂塔的值。 $$f(x) \equiv \begin{cases} a_x^{f(x+1)} & f(x+1) < \varphi(m) \\ a_x^{f(x+1) \bmod \varphi(m) + \varphi(m)} & f(x+1) \ge \varphi(m) \end{cases} \pmod m$$ 根据这个式子,发现事实上我们并不需要真的求出 $f(x+1)$ 的值,我们只需要知道 $f(x+1) \bmod \varphi(m)$ 以及 $f(x+1)$ 和 $\varphi(m)$ 的大小关系。 坑点:有的人求出了 $b$ 对 $\varphi(m)$ 取模的值,然后和 $\varphi(m)$ 比大小,这显然是没有意义且不符合扩展欧拉定理式子的。这个地方杀了一大车题解:[hack](https://www.luogu.com.cn/discuss/655151)。 ## 代码 因为 $\varphi$ 的特性,所以递归层数是 $\log$ 级别的,不会超时。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long LL; typedef __int128 int128; LL phi(LL n) { LL ret = n; for (LL i = 2; i * i <= n; i++) { if (n % i == 0) { ret = ret / i * (i - 1); while (n % i == 0) n /= i; } } if (n != 1) ret = ret / n * (n - 1); return ret; } int128 qpow(int128 a, int128 b, int128 p) { // 在快速幂的同时保住 a^b 和 p 的大小关系 int128 ret = 1; while (b) { if (b & 1) { ret *= a; if (ret >= p) ret = ret % p + p; } a *= a; if (a >= p) a = a % p + p; b >>= 1; } return ret; } const int MAXN = 1234567 + 5; int n; LL a[MAXN]; LL f(int i, LL p) { if (i == n+1 || p == 1) return 1; return qpow(a[i], f(i+1, phi(p)), p); } const int MOD = 10007; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); LL ans = f(1, MOD); printf("%d\n", ans % MOD); return 0; } ``` ## 练习题 [POWTOW - Power Tower City](https://www.luogu.com.cn/problem/SP10050)