[学习笔记]原根
[学习笔记]原根
前言
这里只是简要概述一下,我也不会证明,防止忘记。
一些定义
1.欧拉定理
\[a^{\varphi(m)}\equiv 1 \bmod m,a\perp m
\]
2. 阶
满足 \(a^n\equiv1\bmod m\) 的最小正整数 \(n\) 称为 a 模 m 的阶 ,记作 \(\delta_m(a)\).
于是有恒等式 \(a^{\delta_m(a)}\equiv1\bmod m\)
3.原根
\[m\in N^+,a\in Z,a\perp m,\delta_m(a)=\varphi(m)
\]
同时满足这四个条件的称 a 为 m 的原根。
容易知道可以将所有 a 映射到 \([1,m-1]\) 中
并且原根还有一个重要的性质:
\[a^0,a^1,a^2,a^3.....a^{\varphi(p)-1}\bmod p,互不相等
\]
阶的性质
性质1
\[a^n\equiv1\bmod m\Rightarrow \delta_m(a)|n
\]
性质2
\(\delta_m\) 是一个积性函数
性质3
\[a\perp m \Rightarrow\delta_m(a^k)=\frac{\delta_ma}{gcd(\delta_ma,k)}
\]
关于原根的定理
原根的分布
有且仅有1,2,4,奇素数的正整数幂和幂的两倍有原根
原根的判断
对于一个 \(a\) 满足 \(a\perp m\) 且
\[\forall p|\varphi(m),a^{\frac{\varphi(m)}{p}}\not\equiv 1\bmod m
\]
那么a是m的一个原根
原根的扩散
基于一个最小的原根 g 可以得出其他所有的原根。那些原根\(g'\)满足
\[g'\equiv g^x\bmod m , x\in(1,\varphi(m))且x\perp \varphi(m)
\]
CODE
Luogu 的模板而已
const int MAXN=1e6+5;
bool has[MAXN],isp[MAXN];
int prime[MAXN],t,n,d,pphi[MAXN],ans[MAXN];
inline int get_phi(int x){
int res=x;
pphi[0]=0;
for(int i=1;prime[i]*prime[i]<=x;++i){
if(x%prime[i]==0)res=res/prime[i]*(prime[i]-1);
while(x%prime[i]==0)x/=prime[i];
}
if(x>1)res=res/x*(x-1);
for(int i=1;prime[i]<=res;++i)if(res%prime[i]==0)pphi[++pphi[0]]=prime[i];
return res;
}
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int ksm(int a,int b,int mod){
int res=1;
for(;b;b>>=1,a=(a*a)%mod)if(b&1)res=(res*a)%mod;
return res;
}
signed main(){
has[1]=has[2]=has[4]=1;
fe(i,2,MAXN){
if(!isp[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&prime[j]*i<MAXN;++j){
isp[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
fe(i,2,prime[0]){
int po=prime[i];
while(po<MAXN){
has[po]=1;
if(po*2<MAXN)has[po*2]=1;
po*=prime[i];
}
}
t=read();
while(t--){
n=read(),d=read();
ans[0]=0;
if(!has[n]){puts("0"),puts("");continue;}
int phi=get_phi(n);
int g;
for(g=1;g<=phi;++g)if(gcd(g,n)==1){
int fl=0;
fe(j,1,pphi[0])if(ksm(g,phi/pphi[j],n)==1){
fl=1;break;
}if(!fl)break;
}
int base=g;
for(int x=1;x<=phi;++x,g=(g*base)%n)if(gcd(x,phi)==1)ans[++ans[0]]=g;
sort(ans+1,ans+ans[0]+1);
printf("%lld\n",ans[0]);
for(int i=d;i<=ans[0];i+=d)printf("%lld ",ans[i]);
printf("\n");
}
return 0;
}