洛谷P4139 上帝与集合的正确用法 题解
题目链接:P4139 上帝与集合的正确用法
题意:
有无穷数列
a 0 = 1 , a n = 2 a n − 1 a_0=1,a_n=2^{a_{n-1}} a0=1,an=2an−1
当 n → + ∞ n \to +\infty n→+∞ 时,计算
a n m o d p a_n \bmod p anmodp
即
2 2 2 . . . 2 m o d p {2}^{{2}^{{2}^{{.}^{{.}^{{.}^{2}}}}}} \bmod p 222...2modp
可以证明 a n m o d p a_n\bmod p anmodp在 n n n 足够大时为常数多组数据 1 ≤ Q ≤ 1 0 3 1\le Q\le 10^3 1≤Q≤103 , 1 ≤ p ≤ 1 0 7 1 \le p\le 10^7 1≤p≤107
其实这个题很简单
根据扩展欧拉定理,有
a
b
≡
{
a
b
,
b
<
φ
(
m
)
a
b
m
o
d
φ
(
m
)
+
φ
(
m
)
,
b
≥
φ
(
m
)
(
m
o
d
m
)
\begin{aligned} 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} \end{aligned}
ab≡⎩⎪⎨⎪⎧ab,ab mod φ(m)+φ(m),b<φ(m)b≥φ(m)(modm)
显然当
n
n
n 足够大时,有
a
n
−
1
>
φ
(
p
)
a_{n-1} > \varphi(p)
an−1>φ(p)
则
a
n
m
o
d
p
=
2
a
n
−
1
m
o
d
φ
(
p
)
+
φ
(
p
)
m
o
d
p
a_{n}\bmod p = 2^{a_{n-1}\bmod\, \varphi(p)+\varphi(p)}\bmod p
anmodp=2an−1modφ(p)+φ(p)modp
可以发现原问题转化为了求解
a
n
−
1
m
o
d
φ
(
p
)
+
φ
(
p
)
a_{n-1} \bmod \varphi(p) + \varphi(p)
an−1modφ(p)+φ(p)
递归处理即可
关于这个递归为什么是 O ( log p ) O(\log p) O(logp)
显然每一次递归 p p p 都会变成 φ ( p ) \varphi(p) φ(p)
根据欧拉函数的公式
设
n
=
∏
i
=
1
s
p
i
k
i
n=\prod_{i=1}^{s}p_i^{k_i}
n=∏i=1spiki
φ
(
n
)
=
n
∏
i
=
1
s
(
p
i
−
1
p
i
)
\varphi(n) = n \prod\limits_{i=1}^s\left(\dfrac{p_i-1}{p_i}\right)
φ(n)=ni=1∏s(pipi−1)
观察函数,可以发现
- 当 n n n 为偶数时,一定会把 2 2 2 提出来变成 1 1 1 ,也就是至少除以 2 2 2
- 当 n n n 为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 2 2 2 这个因子
- 重复以上的过程直到 n = 1 n=1 n=1 结束
因此是 O ( log p ) O(\log p) O(logp) 的
所以总的时间复杂度为 O ( Q p log p ) O(Q\sqrt{p}\log p) O(Qplogp)
不用线性筛跑得反而快,因为 p p p 蛮大的,用了就变成 O ( p + Q log p ) O(p+Q\log p) O(p+Qlogp) 了
数比较大,快速幂可能会爆了
long long
\text{long long}
long long ,所以要用龟速乘或者__int128
代码如下
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
int phi(int n)
{
int ans=n;
for(int i=2; i<=n/i; i++)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0)n/=i;
}
if(n>1)ans=ans/n*(n-1);
return ans;
}
int qpow(int a,int b,int p)
{
int ans=1,base=a%p;
while(b)
{
if(b&1)ans=(__int128)ans*base%p;
base=(__int128)base*base%p;
b>>=1;
}
return ans;
}
int solve(int p)
{
if(p==1)return 0;
return qpow(2,solve(phi(p))+phi(p),p);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int Q,p;
cin >> Q;
while(Q--)
{
cin >> p;
cout << solve(p) << endl;
}
return 0;
}
用筛法求
φ
(
p
)
\varphi(p)
φ(p) 跑了 1.52s
,非筛法反倒就跑了114ms
转载请说明出处