1. 程式人生 > >BZOJ3884題解上帝與集合的正確用法--擴展歐拉定理

BZOJ3884題解上帝與集合的正確用法--擴展歐拉定理

裸題 splay inline 線性 lse eof define 用法 int

題目鏈接

https://www.lydsy.com/JudgeOnline/problem.php?id=3884

分析

擴展歐拉定理裸題

  • 歐拉定理及證明:

    如果\((a,m)=1\),則\(a^{\phi(m)} \equiv 1 \mod m\)

    \(Prove:\)\(x\)取遍\(m\)的縮系,則\(ax\)取遍\(m\)的縮系,即

    \[\prod x = \prod ax \mod m\]

    因為這樣的\(a\)\(\phi(m)\)

    \[\prod x = \prod x *a^{ \phi(m)} \mod m\]

    由於\((x,m)=1\),保證\(\prod x\) 存在模\(m\)

    意義下的逆元

    所以 \[a^{ \phi(m)} \equiv 1 \mod m\]

  • 擴展歐拉定理:

    如果 \[(a,m)!=1\]
    \[a^b \equiv a^{min(b,b \% \phi(m)+\phi(m))} \mod m\]

    \(f(x)\)為在模\(x\)意義下題目式子的值,那麽\[f(x)=2^{2^{^{...}}\%\phi(x)+\phi(x)} \mod x=2^{f(\phi(x))+\phi(x)} \mod x\]

    然後就可以記憶化搞一搞了

註意

求歐拉函數可以線性預處理也可以直接求,實踐證明直接求不知道快到哪裏去了

代碼:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define ll long long 
#define ri register int 
using std::sort;
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c==‘-‘;
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;return ;
}
const int maxn=10000005;
const int inf=0x7fffffff;
int t,n;
int mem[maxn];
int phi[maxn];
bool vis[maxn];
inline void get_table(){
    bool is_pri[maxn];
    int num[1000005],tot=0,tmp;
    memset(is_pri,0,sizeof(is_pri));
    is_pri[1]=1;
    phi[1]=1;
    for(ri i=2;i<=maxn;i++){
        //printf("%d\n",i);
        if(!is_pri[i]){
            num[++tot]=i;
            phi[i]=i-1;
        }
        for(ri j=1;j<=tot;j++){
            tmp=num[j]*i;
            if(tmp>=maxn)break;
            is_pri[tmp]=1;
            if(i%num[j]==0){
                phi[tmp]=num[j]*phi[i];
                break;
            }
            else {
                phi[tmp]=(num[j]-1)*phi[i];
            }
        }
    }
    return ;
}
inline int get_phi(int x){
    int res=x;
    for(ri i=2;i*i<=n;i++){
        if(x%i==0){
            res=res/i*(i-1);
            while(x%i==0)x=x/i;
        }
    }
    if(x>1)res=res/x*(x-1);
    return res;
}
int ksm(ll x,int c,int p){
    ll ans=1,res=x;
    while(c){
        if(c&1)ans=ans*res%p;
        res=res*res%p;
        c=c>>1;
    }
    return ans%p;
}
int f(int x){
    if(x==1)return 0;
    if(vis[x])return mem[x];
    int p=phi[x];//int p=get_phi(x);
    vis[x]=1;
    mem[x]=ksm(2,f(p)+p,x);
    return mem[x];
}
int main(){
    read(t);
    get_table();
    while(t--){
        read(n);
        printf("%d\n",f(n));
    }
    return 0;
}

BZOJ3884題解上帝與集合的正確用法--擴展歐拉定理