题解 P4139 上帝与集合的正确用法

题目描述:

Link

\(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;
}
posted @ 2021-01-11 22:15  recollector  阅读(111)  评论(0编辑  收藏  举报