定义
阶:对于 a,m∈N∗,gcd(a,m)=1 若 n 为最小的正整数满足:
an≡1(modm)
称 n 为 a 模 m 的阶,记作 δm(a) 。
显然若 an≡1(modm) ,则 δm(a)∣n
原根:g,m∈N∗ 。若 gcd(g,m)=1 且 δm(g)=φ(m) 则称 g 为模 m 的原根。
性质 1
设 m,a,b∈N∗,gcd(a,m)=gcd(b,m)=1
δm(ab)=δm(a)δm(b)⟺gcd(δm(a),δm(b))=1
性质 2
设 k∈N,a,m∈N∗,gcd(a,m)=1
δm(ak)=gcd(δm(a),k)δm(a)
原根存在定理
模 m 有原根,需满足 m=2,4,pα,2pα (其中 p 为奇素数,α 为正整数 )
原根判定定理
gcd(g,m)=1 ,设 p1,p2,…,pk 为 φ(m) 所有不同素因数
g是m原根⟺∀i∈[1,k],gpiφ(m)≡1(modm)
求所有原根
设 g 为 m 的一个原根,则集合 S={gs∣1≤s≤φ(m),gcd(s,φ(m))=1} 给出 m 的全部原根。因此,若 m 有原根,则 m 有 φ(φ(m)) 个关于模 m 两两互不同余的原根。
若一个数 m 存在原根,可以证明 m 的最小原根在 O(m0.25) 级别。
求所有的原根时,枚举最小原根花费的时间一般都是可以接受的
CODE
luogu-P6091【模板】原根
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
   |  #include <bits/stdc++.h> using namespace std; const int N = 1000010; int p[N],tot,phi[N]; bool v[N],has[N];  int Ts; typedef long long ll; ll fpow(ll a,ll b,ll p) {ll r=1;for(;b;b>>=1,a=(a*a)%p) if(b&1) r=(r*a)%p; return r;} int gcd(int a,int b) {return !b?a:gcd(b,a%b);} void init(int n) {      phi[1]=1;     for(int i=2;i<=n;++i) {         if(!v[i]) p[++tot]=i,phi[i]=i-1;         for(int j=1;j<=tot&&p[j]*i<=n;++j) {             v[p[j]*i]=1;             if(i%p[j]==0) {                 phi[i*p[j]]=phi[i]*p[j];                 break;             }             phi[i*p[j]]=phi[i]*(p[j]-1);         }     }     has[2]=has[4]=1;     for(int i=2;i<=tot;++i) for(ll j=p[i];j<=n;j*=p[i]) has[j]=1,(j*2<=n)&&(has[j*2]=1); } void devide(int x,vector<int> &ret) {      ret.clear();     for(int i=1;1ll*p[i]*p[i]<=x;++i) if(x%p[i]==0){         ret.push_back(p[i]);         while(x%p[i]==0) x/=p[i];     }     if(x>1) ret.push_back(x); } bool hasroot(int n) {      if(n==2||n==4) return 1;     if(n%2==0) n/=2;     if(n%2==0) return 0;     for(int i=2;1ll*p[i]*p[i]<=n;++i) if(n%p[i]==0) {         while(n%p[i]==0) n/=p[i];         return (n==1);       }     return 1; } void Getroot(int m,vector<int> &res) {     res.clear();     if(!has[m]) return ;     vector<int> factor;     devide(phi[m],factor);     int g=1,mphi=phi[m];     while(true) {         bool fg=1;         if(gcd(g,m)!=1) fg=0;          else for(int i=0;i<(int)factor.size();++i) {             if(fpow(g,mphi/factor[i],m)==1ll) {fg=0;break;}         }         if(fg) break;         ++g;     }     ll pw=1;     for(int s=1;(int)res.size()<phi[mphi];++s) {         pw=(pw*g)%m;         if(gcd(s,mphi)==1) res.push_back(pw);     }     sort(res.begin(),res.end()); } int main() {     init(1e6);     scanf("%d",&Ts);     while(Ts--) {         int n,d;          scanf("%d%d",&n,&d);         vector<int> ans;         Getroot(n,ans);         printf("%d\n",(int)ans.size());         for(int i=d-1;i<(int)ans.size();i+=d) printf("%d ",ans[i]);         printf("\n");     } }
 
  | 
 
Reference
Oi-wiki 原根