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

Portal-->bzoj3884

Solution

  这个。。额。。如果知道扩展欧拉定理的话这题其实。。比较裸的样子

  虽然说无限个(2)听起来就很恐怖但是

  根据扩展欧拉定理,当(b>p)时,有:

[a^bequiv 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
",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;
}
原文地址:https://www.cnblogs.com/yoyoball/p/9220623.html