题解 P4139 上帝与集合的正确用法
题目描述:
求 \(2^{2^{2^{\cdots}}}\bmod p\) 的值。
\(T\) 组数据, \(1\leq T \leq 10^3 ,1\leq p \leq 10^7\) 。
前置知识:
欧拉定理:若 \(a,p\) 互质,则 \(a^b \bmod p = a ^{b \bmod \varphi(p)}\bmod p\) 。
拓展欧拉定理:若不保证 \(a,p\) 互质,则:
\[a^b \bmod p=
\begin{cases}
a^b (b < \varphi(p)) \\
a^{b \bmod \varphi(p)+\varphi(p)} (b \geq \varphi(p))
\end{cases}
\]
Solution
看到这个无解的式子,我们考虑拓展拉定理求解。
因为这个式子有无限个,所以 \(b\) 远远大于 \(\varphi(p)\) ,所以要使用上面 \(a^b \bmod p=a^{b \bmod \varphi(p)+\varphi(p)}\) 这个公式
我们可以把原式子变成:
\[2^{2\bmod \varphi(p)+\varphi(p)^{2\bmod \varphi(\varphi(p))+\varphi(\varphi(p))}\cdots}
\]
发现这是个递归的式子,为了方便,我们设:
\[f(p) = 2^{2^{2^{\cdots}}} \bmod p
\]
这样,根据拓展欧拉定理:
\[f(p)=2^{2^{2}{\cdots} \bmod \varphi(p)+\varphi(p)} \bmod p \\
=2^{f(\varphi(p))+\varphi(p)} \bmod p
\]
边界是 \(p=1\) 时,返回 \(0\) ,这是因为,当 \(p=1\) 是,任何数除以 \(1\) 的余数都是 \(0\) 。
剩下的可以用快速幂来解决。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#define int long long
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
const int N = 1e7 + 5;
int prime[(int)(1e6) + 5] ,cnt; bool isprime[N];
int phi[N];
inline void Prime(int n) { //线性筛求 phi
memset(isprime ,true ,sizeof(isprime));
isprime[0] = isprime[1] = false; phi[1] = 1;
for (int i = 2;i <= n; i++) {
if (isprime[i]) {
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1;j <= cnt && i * prime[j] <= n; j++) {
isprime[i * prime[j]] = false;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
inline int pow(int a ,int b ,int p) {
//calculate a ^ b mod p
int ans = 1 % p;
while (b) {
if (b & 1) ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
inline int solve(int p) {
//计算 2 的 2 的 2 的 2 ...... 次方
if (p == 1) return 0; //边界
return pow(2 ,solve(phi[p]) + phi[p] ,p); // 把公式抄下来
}
int T ,p;
signed main() {
Prime(1e7); //一定要先预处理,不然......
T = read();
while (T--) {
p = read();
printf("%lld\n" ,solve(p));
}
return 0;
}