【bzoj3884】上帝与集合的正确用法
Solution
这个。。额。。如果知道扩展欧拉定理的话这题其实。。比较裸的样子
虽然说无限个\(2\)听起来就很恐怖但是
根据扩展欧拉定理,当\(b>p\)时,有:
\[a^b\equiv a^{b\%\varphi(p)+\varphi(p)}(mod\ p)
\]
然后看一下那个无限个\(2\)翻上去的指数。。很明显是\(>p\)的所以。。这条式子就可以直接用啦
每次我们都用这条式子去进行一个类似降幂的操作,然后模数到到后面会长成:
\[\varphi(\varphi(\varphi(...\varphi(p)))))
\]
这样。。
进行若干次操作之后会变成\(1\),那么这个时候无论后面再怎么降下去结果都是固定的了
至于这个若干次操作到底是多少,我们可以感性的理解一下(理性证明不会qwq),\(\varphi(p)\)会不断缩小然后每次至少会除去一个\(2\),所以最多是\(log\)级别的
那所以直接递归求解就好了
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=1e7+10;
int phi[MAXN],p[MAXN];
bool vis[MAXN];
int T,n;
void prework(int n);
int f(int n);
int ksm(int x,int y,int p);
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&T);
prework(MAXN-1);
for (int o=1;o<=T;++o){
scanf("%d",&n);
printf("%d\n",f(n));
}
}
void prework(int n){
int cnt=0;
phi[1]=1;
for (int i=2;i<=n;++i){
if (!vis[i]){
phi[i]=i-1;
p[++cnt]=i;
}
for (int j=1;j<=cnt&&p[j]*i<=n;++j){
vis[i*p[j]]=true;
if (i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
else
phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
int f(int p){
if (p==1) return 0;
return ksm(2,f(phi[p])+phi[p],p);
}
int ksm(int x,int y,int mod){
int ret=1,base=x;
for (;y;y>>=1,base=1LL*base*base%mod)
if (y&1) ret=1LL*ret*base%mod;
return ret;
}