【BZOJ3884】上帝與集合的正確用法
阿新 • • 發佈:2018-06-21
pan 相對 spa printf 可能 mat 一次 返回 space 的\(2^{2...}\),只不過模數由\(p\)變成\(\varphi(q)\)。當模數\(p\)變成1的時候,我們就遇到了邊界——不管裏面式子如何,模1都是0,直接返回0即可。考慮遞歸的層數:除了第一次調用的\(p\)可能是奇數之外,往下遞歸的\(p\)幾乎都是偶數(\(\varphi(x),x\ge3\)都是偶數),\(\varphi(q)\)相對於\(p\)大概會減少一倍。直到\(p=1\)時,層數不會太多,dalao說是\(O(log^2p\))。
所以就直接遞歸計算了。實現上,如果先用線性篩篩出所有的\(\varphi\),太慢。每次調用\(\varphi\)時直接\(O(\sqrt n)\) 計算反而更加快。這兩種方法,是穩定300ms和6ms的差距......
Description
一句話題意,給定\(p\)作為模數:
\(p\le 10^7\),數據組數\(T\le1000\)。
Solution
看到就棄療了,再見......
將模數\(p\)拆分成\(p=q2^k\),其中\(q\)為一個奇數。那麽:
\[
\begin{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)\)
所以就直接遞歸計算了。實現上,如果先用線性篩篩出所有的\(\varphi\),太慢。每次調用\(\varphi\)時直接\(O(\sqrt n)\)
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\n",calc(p));
}
return 0;
}
【BZOJ3884】上帝與集合的正確用法