原根
我也不知道为什么我求原根的板子都没打就来学ntt(
好吧其实知道原根是啥就行
阶
定义:若 \(\gcd(a,n)=1\) ,则满足 \(a^{x}\equiv 1 \pmod n\) 的最小正整数 \(x\) 称为 \(a\) 模 \(n\) 的阶,记作 \(\text{ord}_n(a)\) 。
性质:
- \(a,a^2,a^3,\cdots,a^{\text{ord}_n(a)}\) 在模 \(n\) 意义下两两不同。(所以ntt用原根替代单位根)
- 若 \(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)}\) 。 - 阶是积性函数。
- \(\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");
}
}
}
快踩