Definition
-
指数:设 (m>1) 为整数, (a) 为与 (m) 互素的数,则使得 (a^e equiv 1 (mod m)) 成立的是最小正整数 (e) 叫做 (a) 对模 (m) 的指数,记作 (ord_{m}{a})
-
原根:若 (a) 对 模 (m) 的指数为 (varphi(m)) ,则 (a) 叫做模 (m) 的原根
定理
-
设 (m>1) 是整数,(a) 是与 (m) 互素的整数,则整数 (d) 使得 (a^d equiv 1 (mod 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 (mod 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
",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
",tot);
// printf("qwq %lld
",phi[n]);
if(!yg[n])
{
printf("0
");
continue;
}
ll minn=getyg(n);
// printf("zzz %lld
",minn);
qj(n,minn);
std::sort(ans+1,ans+sum+1);
printf("%lld
",sum);
for(int i=d;i<=sum;i+=d)
{
printf("%lld ",ans[i]);
}
printf("
");
}
return 0;
}