P6091原根
题意:求\(n(<=1e6)\)的所有原根
原根定义(图片来自百度百科)

什么样的数有原根?
\(2,4,p^k,2*p^k,p\)奇素数,k正整数
怎么求?
找到最小原根g,其余原根为\(g^k\)
若n存在原根,个数为\(\phi(\phi(n))\),满足\(gcd(k,\phi(n))=1\)
如何找最小原根?
从小到大枚举,最小原根是\(O(n^{\frac 1 4})\)
如何检测?
\(g^{\phi(n)}\equiv1,g^k\not\equiv1(k<\phi(n))\)
关于阶的一条性质:若\(gcd(a,n)=1\),且\(a^k\equiv1\pmod n\),则\(k|\phi(n)\),只需检测\(\phi(n)\)的真因子即可
时间复杂度\(O(n^{\frac14}logn+\phi(n)log\phi(n))\)
步骤:
-
线性筛预处理所有质数和有原根的数
-
将\(\phi(n)\)分解因数
-
枚举求出最小原根
-
通过最小原根求出所有原根
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
#define ll long long
const int N=1e6+4;
inline int ksm(int x,int r,int mod){
int ret=1;
for(int i=0;(1<<i)<=r;i++){
if((r>>i)&1)ret=(ll)ret*x%mod;
x=(ll)x*x%mod;
}
return ret;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int n,d,cnt,tot,g,fl;
int pri[N],don[N],b[N],phi[N],ans[N],yin[N];
inline void init(){
for(int i=2;i<N;i++){
if(!b[i]){
pri[++tot]=i;
phi[i]=i-1;
if(i&1)don[i]=1;
}
for(int j=1;j<=tot&&pri[j]*i<N;j++){
b[pri[j]*i]=1;
if(i%pri[j]==0){
phi[i*pri[j]]=phi[i]*pri[j];
if(don[i])don[pri[j]*i]=1;
break;
}
else phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}
for(int i=(N-1)/2;i;i--)
if(don[i])don[i<<1]=1;
don[2]=don[4]=1;
}
int main(){
init();
int T=read();
while(T--){
n=read();d=read();
if(!don[n]){puts("0\n");continue;}
ans[0]=cnt=g=0;
for(int i=1;i<=tot&&pri[i]*pri[i]<=phi[n];i++)
if(phi[n]%pri[i]==0)yin[++cnt]=phi[n]/pri[i];
if(phi[n]>1)yin[++cnt]=1;
while(++g){
if(ksm(g,phi[n],n)!=1)continue;
fl=1;
for(int i=1;i<=cnt;i++)
if(ksm(g,yin[i],n)==1){fl=0;break;}
if(fl)break;
}
ans[1]=g;
for(int i=1;i<=phi[n];i++){
if(gcd(i,phi[n])!=1)continue;
ans[++ans[0]]=ksm(g,i,n);
}
sort(ans+1,ans+ans[0]+1);
cout<<ans[0]<<"\n";
for(int i=d;i<=ans[0];i+=d)cout<<ans[i]<<" ";
puts("");
}
return (0-0);
}
作者:starusc
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号