Description
一句话题意,给定(p)作为模数:
(ple 10^7),数据组数(Tle1000)。
Solution
看到就弃疗了,再见......
将模数(p)拆分成(p=q2^k),其中(q)为一个奇数。那么:
[egin{aligned}
2^{2^{2...}}mod; p&=2^k(2^{2^{2..}-k}mod;q)\
&=2^k(2^{(2^{2..}-k)mod;varphi(q)}mod;q)
end{aligned}
]
考虑递归计算((2^{2...}-k))的(2^{2...}),只不过模数由(p)变成(varphi(q))。当模数(p)变成1的时候,我们就遇到了边界——不管里面式子如何,模1都是0,直接返回0即可。考虑递归的层数:除了第一次调用的(p)可能是奇数之外,往下递归的(p)几乎都是偶数((varphi(x),xge3)都是偶数),(varphi(q))相对于(p)大概会减少一倍。直到(p=1)时,层数不会太多,dalao说是(O(log^2p))。
所以就直接递归计算了。实现上,如果先用线性筛筛出所有的(varphi),太慢。每次调用(varphi)时直接(O(sqrt n))计算反而更加快。这两种方法,是稳定300ms和6ms的差距......
Code
#include <cstdio>
using namespace std;
const int S=10000001;
inline int ksm(int x,int y,int MOD){
int res=1;
for(;y;x=1LL*x*x%MOD,y>>=1)
if(y&1) res=1LL*res*x%MOD;
return res;
}
int getPhi(int x){
int res=x;
for(int i=2;i*i<=x;i++){
if(!(x%i)) res-=res/i;
while(!(x%i)) x/=i;
}
if(x!=1) res-=res/x;
return res;
}
int calc(int p){
if(p==1) return 0;
int k=0,q=p;
while(!(q&1)) k++,q>>=1;
int phiq=getPhi(q);
int mi=(calc(phiq)-k)%phiq;
if(mi<0) mi+=phiq;
return 1LL*ksm(2,mi,q)*ksm(2,k,p)%p;
}
int main(){
int T,p;
scanf("%d",&T);
while(T--){
scanf("%d",&p);
printf("%d
",calc(p));
}
return 0;
}