1. 程式人生 > 實用技巧 >題解 洛谷P4139 上帝與集合的正確用法

題解 洛谷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;
}