原根
Definition
-
指数:设 \(m>1\) 为整数, \(a\) 为与 \(m\) 互素的数,则使得 \(a^e \equiv 1 (\bmod m)\) 成立的是最小正整数 \(e\) 叫做 \(a\) 对模 \(m\) 的指数,记作 \(ord_{m}{a}\)
-
原根:若 \(a\) 对 模 \(m\) 的指数为 \(\varphi(m)\) ,则 \(a\) 叫做模 \(m\) 的原根
定理
-
设 \(m>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,则整数 \(d\) 使得 \(a^d \equiv 1 (\bmod m)\) 成立的充分必要条件为 \(ord_m(a) | d\)
-
设 \(m>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,则
\[1 = a^0 , a , ....... , a^{ord_m(a)-1} \]模 \(m\) 两两不同于,特别的,当 \(a\) 为 \(m\) 的原根时,有 \(ord_m(a) = \varphi(m)\) ,这 \(\varphi(m)\) 个数组成模 \(m\) 的简化剩余系
-
设 \(m>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,设 \(d\) 为非负整数,则
\[ord_m{(a^d)} = \frac{ord_m(a)}{(d,ord_m(a))} \]故有推论:设 \(m>1\) 是整数,\(g\) 是模 \(m\) 的原根,设 \(d \geq 0\) 为整数,则 \(g^d\) 为模 \(m\) 的原根当且仅当 \((d,\varphi(m)) = 1\)
-
设 \(m>1\) 是整数,如果模 \(m\) 存在一个原根 \(g\) ,则模 \(m\) 的原根有 \(\varphi(\varphi(m))\) 个不同的原根
-
设 \(m>1\) 是整数,\(a\) , \(b\) 是与 \(m\) 互素的整数,如果 \((ord_m(a),ord_m(b)) = 1\) ,则
\[ord_m(a,b) = ord_m(a) · ord_m(b) \]成立,反之亦然
-
设 \(m,n>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,则:若 \(n|m\) ,则 \(ord_n(a) | ord_m(a)\)
同时,若 \((n,m) =1\) ,则
\[ord_{mn}(a)=[ord_m(a),ord_n(a)] \] -
模 \(p\) 原根存在的充要条件为 \(m=2 , 4 , p^a , 2p^a\)
例题
分析
对于本题,首先我们可以应用定理7,那么所有的不能表示为 \(2,4,p^a,2p^a\) 的数都没有原根
其次,我们可以求出最小原根,然后通过定理3的推论,将所有的原根求出
那么如何求最小原根呢,首先想到的方法便是直接扫描判断,一般而言,这个方法就已经很好了,因为大部分数的最小原根要么没有,要么很小
那么判断时,我们枚举到的当前数字 \(g\) ,显然有 \(g^{\varphi(m)} \equiv 1 (\bmod m)\) (欧拉定理),那么,我们可以应用定理1,将 \(\varphi(m)\) 进行质因数分解,设其质因数为\(p_i\),如果 \(g\) 是 \(m\) 的一个原根,那么它的\(\frac{\varphi(m)}{p_i}\) 次方必然在模 \(m\) 意义下不为 \(1\) , 对于所有质因子都满足上述条件的 \(g\) ,即为最小原根
Solution
通过分析,我们可以先用线性筛将 \(\varphi(i)\) 的大小以及有原根的数筛选出来
之后,我们就可以通过依次判断求得最小原根,然后从 \(1\) ~ \(\varphi(m)\) 进行枚举,挑选出其中与 \(\varphi(m)\) 互素的数作为指数,利用快速幂处理得到其他的原根
code
#include<iostream>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<algorithm>
#include<map>
#include<bitset>
#include<queue>
#include<vector>
#define ll long long
const ll maxn=1e6+10;
ll t,n,cnt,d,tot,sum;
ll vis[maxn],prime[maxn],phi[maxn],yg[maxn],yz[maxn],ans[maxn];
inline void cle()
{
tot=sum=0;
}
inline ll gcd(ll a,ll b)
{
return b==0 ? a : gcd(b,a%b);
}
inline void pre(ll x)
{
phi[1]=1;
for(int i=2;i<=x;i++)
{
if(!vis[i])
{
vis[i]=1;
prime[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=x;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
yg[2]=yg[4]=1;
for(int i=2;i<=cnt;i++)
{
for(int j=1;j*prime[i]<=x;j*=prime[i]) yg[j*prime[i]]=1;
for(int j=2;j*prime[i]<=x;j*=prime[i]) yg[j*prime[i]]=1;
}
}
inline ll ksm(ll a,ll b,ll p)
{
ll ret=1;
while(b)
{
if(b&1) ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return (ret%p);
}
inline void fj(ll x)
{
for(int i=1;i<=cnt&&prime[i]<=x;i++)
{
if(x%prime[i]==0) yz[++tot]=prime[i];
}
}
inline ll check(ll ds,ll x)
{
if(ksm(ds,phi[x],x)!=1) return 0;
for(int i=1;i<=tot;i++)
{
if(ksm(ds,phi[x]/yz[i],x)==1) return 0;
}
return 1;
}
inline ll getyg(ll x)
{
for(int i=1;i<=x;i++)
{
// printf("123 %lld\n",i);
if(check(i,x))
{
return i;
}
}
// return 0;
}
inline void qj(ll p,ll x)
{
ll ret=1;
for(int i=1;i<=phi[p];i++)
{
ret=ret*x%p;
if(gcd(i,phi[p])==1) ans[++sum]=ret;
}
}
int main(void)
{
// freopen("2.in","r",stdin);
// freopen("2.out","w",stdout);
scanf("%lld",&t);
pre(maxn-10);
while(t--)
{
cle();
scanf("%lld %lld",&n,&d);
fj(phi[n]);
// printf("qaq %lld\n",tot);
// printf("qwq %lld\n",phi[n]);
if(!yg[n])
{
printf("0\n\n");
continue;
}
ll minn=getyg(n);
// printf("zzz %lld\n",minn);
qj(n,minn);
std::sort(ans+1,ans+sum+1);
printf("%lld\n",sum);
for(int i=d;i<=sum;i+=d)
{
printf("%lld ",ans[i]);
}
printf("\n");
}
return 0;
}