天天看点

原根

数论:原根

首先我们要明确一点

有原根的数,只有四种:\(2、4、p^e、2*p^e\)(这里\(p\)是奇素数,\(e\)是正整数)

阶(同余中的)

定义:设\(m>1\),满足\(gcd(a,m)=1\),使得\(a^x\equiv 1(mod\ m)\)的最小的正整数\(x\)为\(a\)对\(m\)的阶,\(\delta_m(a)\)

欧拉定理:\(a\)与\(m\)互质条件下,\(a^{\varphi(m)}\equiv 1(mod\ m)\)

那么我们得到一个性质:\(\delta_m(a)|\varphi(m)\)

有关阶的更多性质

定义

定义\(m\)的原根\(a\),当且仅当\(a\)对\(m\)的阶是\(\varphi(m)\)

性质

1、每一个有原根的数\(m\),都一共有\(\varphi(\varphi(m))\)个原根

2、对于任意\(\varphi(m)\)的质因子\(p\),都有\(a^{\frac{\varphi(m)}{p}}(mod\ m)\ne 1\)

因为如果可以等于\(1\)的话,说明\(a\)对\(m\)的阶不是\(\varphi(m)\)

3、设原根为\(a\),那么\(a^1,a^2,a^3,...,a^{\varphi(m)}\)两两不同余,并且可以取遍所有与\(m\)互质的\(\varphi(m)\)个数

4、一个数\(m\)的最小原根大小不会超过\(m^{0.25}\)

5、原根一定与\(m\)互质

求原根

如果有原根的话,直接枚举\(a\),判断性质2和定义即可得到最小原根,时间\(\mathcal{O(m^{0.25}lognlogn)}\)

由最小原根可以找到全部的原根,设最小原根为\(a\)

满足\(a^s\)是原根的充要条件是\(gcd(s,\varphi(m))=1\),时间复杂度\(\mathcal{O(\varphi(m)log\varphi(m))}\)

Luogu6091 原根

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e6+5;
int ksm(int x,int y,int mod){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int T,n,d,R=1e6;
bool vis[N],hrt[N];
int p[N],cnt,phi[N];
void pre(){
    fo(i,2,R){
        if(!vis[i])p[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*p[j]<=R;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]];
        }
    }
    hrt[2]=hrt[4]=true;
    fo(i,2,cnt){
        int now=p[i];
        while(now<=R){
            hrt[now]=true;
            if(now*2<=R)hrt[now*2]=true;
            now*=p[i];
        }
    }
}
int pc[N],cpc;
void getpc(int x){cpc=0;x=phi[x];
    for(int i=1;i<=cnt&&p[i]*p[i]<=x;i++){
        if(x%p[i]==0)pc[++cpc]=p[i];
        while(x%p[i]==0)x/=p[i];
    }
    if(x>1)pc[++cpc]=x;
}
bool ck(int r,int x){
    if(ksm(r,phi[x],x)!=1)return false;
    fo(i,1,cpc)if(ksm(r,phi[x]/pc[i],x)==1)return false;
    return true;
}
int minrt(int x){
    fo(i,1,x)if(ck(i,x))return i;
    return 0;
}
int ans[N],an;
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
void getal(int mr,int x){
    int now=1;an=0;
    fo(i,1,phi[x]){
        now=now*mr%x;
        if(gcd(i,phi[x])==1)ans[++an]=now;
    }
}
signed main(){
    T=read();pre();
    while(T--){
        n=read();d=read();
        if(!hrt[n]){printf("0\n\n");continue;}
        getpc(n);int mr=minrt(n);
        getal(mr,n);sort(ans+1,ans+an+1);
        printf("%lld\n",an);
        for(int i=d;i<=an;i+=d)printf("%lld ",ans[i]);
        printf("\n");
    }
}
           

应用

原根可以把数\(y\)换成\(r^x\),\(r\)为原根\(x\)是未知数,这样就把求解\(y\)的过程变成求解\(x\)了,可以使用\(BSGS\)

QQ:2953174821

上一篇: 容斥