原根

Definition

  • 指数:设 \(m>1\) 为整数, \(a\) 为与 \(m\) 互素的数,则使得 \(a^e \equiv 1 (\bmod m)\) 成立的是最小正整数 \(e\) 叫做 \(a\) 对模 \(m\) 的指数,记作 \(ord_{m}{a}\)

  • 原根:若 \(a\) 对 模 \(m\) 的指数为 \(\varphi(m)\) ,则 \(a\) 叫做模 \(m\) 的原根

定理

  1. \(m>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,则整数 \(d\) 使得 \(a^d \equiv 1 (\bmod m)\) 成立的充分必要条件为 \(ord_m(a) | d\)

  2. \(m>1\) 是整数,\(a\) 是与 \(m\) 互素的整数,则

    \[1 = a^0 , a , ....... , a^{ord_m(a)-1} \]

    \(m\) 两两不同于,特别的,当 \(a\)\(m\) 的原根时,有 \(ord_m(a) = \varphi(m)\) ,这 \(\varphi(m)\) 个数组成模 \(m\) 的简化剩余系

  3. \(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\)

  4. \(m>1\) 是整数,如果模 \(m\) 存在一个原根 \(g\) ,则模 \(m\) 的原根有 \(\varphi(\varphi(m))\) 个不同的原根

  5. \(m>1\) 是整数,\(a\) , \(b\) 是与 \(m\) 互素的整数,如果 \((ord_m(a),ord_m(b)) = 1\) ,则

    \[ord_m(a,b) = ord_m(a) · ord_m(b) \]

    成立,反之亦然

  6. \(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)] \]

  7. \(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;
}	
posted @ 2021-01-15 09:50  雾隐  阅读(268)  评论(0编辑  收藏  举报