题意:求(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 otequiv1(k<phi(n)))
关于阶的一条性质:若(gcd(a,n)=1),且(a^kequiv1pmod n),则(k|phi(n)),只需检测(phi(n))的真因子即可
时间复杂度(O(n^{frac14}logn+phi(n)logphi(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
");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]<<"
";
for(int i=d;i<=ans[0];i+=d)cout<<ans[i]<<" ";
puts("");
}
return (0-0);
}