【学习笔记】原根

原根是 \(NTT\) 的前置,想学 \(NTT\) 就得先学求原根。
由于作者个人时间原因,原根直接讲结论。

 
只有\(2,4,p^c,2\times p^c\)有原根,其中 \(c\) 为奇质数。
\(n\) 的原根大概在 \(n^{0.25}\) 左右,且分布密集。
检测 \(p\) 是否是原根,要看对于所有的 \(\phi(n)\) 的质数 \(k\),是否都有 \(p^{\frac{\phi(n)}{k}} \not= 1\)。(理解是除了 \(p^{\phi(n)}\) 其他的次方都不能等于1,而\(p^{\phi(n)}\)包含了所有的因数。)
找到最小原根 \(p\) 后(复杂度 \(n^{0.25} \log{\phi(n)}\)),可以通过 \(p^k\) \((gcd(k,\phi(n))==1)\) 求出所有的原根。原根总个数为 \(\phi(\phi(n))\)

 
luogu模板题代码。

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=10000010,Ma=1e6;
int mod;
int wk[N],zhi[N],tail;
int n,d,fi,ans[N],cnt;
int sum[N],tot;
bool had[N];
int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
int pw(int x,int y){
	int ansn=1;
	while(y){
		if(y&1)ansn=ansn*x%mod;
		x=x*x%mod,y/=2;
	}
	return ansn;
}
void work(){
	n=rd(),d=rd(),cnt=tot=0;
	if(!had[n])return printf("0\n\n"),void();
	mod=n;
	fi=n;
	for(int i=1;i<=tail&&zhi[i]<=n;i++)if(n%zhi[i]==0)fi=fi*(zhi[i]-1)/zhi[i];
	for(int i=1;i<=tail&&zhi[i]<=fi;i++)if(fi%zhi[i]==0)sum[++tot]=zhi[i];
	int gen;
	for(gen=1;gen<n;gen++){
		if(gcd(gen,n)!=1)continue;
		bool flg=true;
		for(int i=1;i<=tot;i++){
			if(pw(gen,fi/sum[i])==1)flg=false;
		}
		if(flg)break;
	}
	for(int i=1,j=gen;i<=fi;i++,j=j*gen%mod){
		if(gcd(i,fi)==1)ans[++cnt]=j;
	}
	sort(ans+1,ans+cnt+1);
	printf("%lld\n",cnt);
	for(int i=d;i<=cnt;i+=d)printf("%d ",ans[i]);
	printf("\n");
	return ;
}
signed main(){
	for(int i=2;i<=Ma;i++){
		if(!wk[i])zhi[++tail]=i;
		for(int j=1;j<=tail&&i*zhi[j]<=Ma;j++){
			wk[zhi[j]*i]=true;
			if(i%zhi[j]==0)break;
		}
	}
	had[2]=had[4]=true;
	for(int i=2;i<=tail;i++){
		for(int j=1;j*zhi[i]<=Ma;j=j*zhi[i])had[j*zhi[i]]=true;
		for(int j=2;j*zhi[i]<=Ma;j=j*zhi[i])had[j*zhi[i]]=true;
	}
	int T=rd();
	while(T--)work();
	return 0;
}
/*
1
36 1
*/
posted @ 2023-04-25 09:42  flywatre  阅读(50)  评论(0)    收藏  举报