原根

我也不知道为什么我求原根的板子都没打就来学ntt(

好吧其实知道原根是啥就行

定义:若 \(\gcd(a,n)=1\) ,则满足 \(a^{x}\equiv 1 \pmod n\) 的最小正整数 \(x\) 称为 \(a\)\(n\) 的阶,记作 \(\text{ord}_n(a)\)

性质:

  1. \(a,a^2,a^3,\cdots,a^{\text{ord}_n(a)}\) 在模 \(n\) 意义下两两不同。(所以ntt用原根替代单位根)
  2. \(a^x\equiv 1 \pmod n\) ,则 \(\text{ord}_n(a)\mid x\)
    一个推论:若 \(a^p\equiv a^q \pmod n\) ,则 \(p\equiv q \pmod {\text{ord}_n(a)}\)
  3. 阶是积性函数。
  4. \(\text{ord}_n(a^k)=\frac{\text{ord}_n(a)}{\gcd(\text{ord}_n(a),k)}\)

原根

如果 \(\text{ord}_n(a)=\varphi(n)\) ,则 \(a\) 为模 \(n\) 的原根。

只有 \(1,2,4,p^k,2p^k\) 有原根( \(p\) 是奇素数, \(k\) 是任意正整数)。

原根的判定:设 \(m\ge 3\) ,则 \(a\) 是模 \(n\) 的原根的充要条件是对 \(\varphi(n)\) 的每个质因数 \(p\) ,有 \(a^{\frac {\varphi(n)}p}\not\equiv 1\pmod n\)

然后一个数的最小原根是 \(n^{0.25}\) 级别的,所以可以暴力找。

然后如果找到了一个数的最小原根 \(g\) ,则所有满足 \(\gcd(k,\varphi(n))\)\(g^k\) 都是模 \(n\) 的原根。

最后来一个洛谷求原根的板子。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define int long long
int n,d,g[1000010],phi[1000010],p[1000010],prm[1000010];
bool v[1000010],rt[1000010];
void get(){
	phi[1]=1;
	for(int i=2;i<=1000000;i++){
		if(!v[i]){
			p[++p[0]]=i;phi[i]=i-1;
		}
		for(int j=1;j<=p[0]&&i*p[j]<=1000000;j++){
			v[i*p[j]]=true;
			if(i%p[j]==0){
				phi[i*p[j]]=phi[i]*p[j];break;
			}
			phi[i*p[j]]=phi[i]*phi[p[j]];
		}
	}
	rt[2]=rt[4]=true;
	for(int i=2;i<=p[0];i++){
		for(int j=p[i];j<=1000000;j=j*p[i])rt[j]=true;
		for(int j=p[i]*2;j<=1000000;j=j*p[i])rt[j]=true;
	}
}
int getphi(int x){
	int ans=x;
	for(int i=2;i*i<=x;i++){
		if(x%i==0){
			ans=ans/i*(i-1);
			while(x%i==0)x/=i;
		}
	}
	if(x!=1)ans=ans/x*(x-1);
	return ans;
}
void getp(int x){
	for(int i=2;i*i<=x;i++){
		if(x%i==0){
			prm[++prm[0]]=i;
			while(x%i==0)x/=i;
		}
	}
	if(x!=1)prm[++prm[0]]=x;
}
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=1ll*ans*a%n;
		a=1ll*a*a%n;
		b>>=1;
	}
	return ans;
}
bool check(int x){
	if(qpow(x,phi[n])!=1)return false;
	for(int i=1;i<=prm[0];i++){
		if(qpow(x,phi[n]/prm[i])==1)return false;
	}
	return true;
}
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
signed main(){
	int tim;scanf("%lld",&tim);
	get();
	while(tim--){
		scanf("%lld%lld",&n,&d);
		if(!rt[n]){
			printf("0\n\n");
		}
		else{
			prm[0]=g[0]=0;
			getp(phi[n]);
			int ret;
			for(int i=1;i<n;i++){
				if(check(i)){
					ret=i;break;
				}
			}
			for(int i=1;i<=phi[n];i++){
				if(gcd(i,phi[n])==1)g[++g[0]]=qpow(ret,i);
			}
			sort(g+1,g+g[0]+1);
			printf("%lld\n",g[0]);
			for(int i=d;i<=g[0];i+=d)printf("%lld ",g[i]);
			printf("\n");
		}
	}
}
posted @ 2022-09-11 16:49  gtm1514  阅读(35)  评论(0编辑  收藏  举报