【bzoj3884】上帝与集合的正确用法

Portal-->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;
}
posted @ 2018-06-24 15:25  yoyoball  阅读(362)  评论(0编辑  收藏  举报