原根是 \(NTT\) 的前置,想学 \(NTT\) 就得先学求原根。
由于作者个人时间原因,原根直接讲结论。
只有\(2,4,p^c,2\times p^c\)有原根,其中 \(c\) 为奇质数。
\(n\) 的原根大概在 \(n^{0.25}\) 左右,且分布密集。
检测 \(p\) 是否是原根,要看对于所有的 \(\phi(n)\) 的质数 \(k\),是否都有 \(p^{\frac{\phi(n)}{k}} != 1\)。(理解是除了 \(p^{\phi(n)}\) 其他的次方都不能等于1)
找到最小原根 \(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
*/
标签:phi,return,原根,int,笔记,学习,ansn,mod
From: https://www.cnblogs.com/flywatre/p/17351704.html