原根
前置知识
欧拉函数
若正整数m,a,满足\((a,m)=1\),则\(a^{\phi(m)}\equiv 1(mod\: m)\)
阶
若正整数m,a,满足\((a,m)=1\),则使得\(a^n\equiv 1(mod \: m)\)的最小正整数n称为a模m的阶,记为\(\delta_m(a)\)
阶的性质
假设\((a,m)=1\),\(\delta=\delta_m(a)\),则
- \(a^0,a^1,···,a^{\delta-1}\)在模m意义下两两不同
- \(a^{\gamma}\equiv a^{\gamma'}(mod\: m)\Leftarrow\Rightarrow \gamma\equiv \gamma' (mod \: \delta)\)
- \(\delta | \phi(m)\)
原根
若\(\delta_m(a)=\phi(m)\),则称a为m的一个原根
原根的存在定理
只有模\(2,4,p^a,2p^a\)存在原根(p是奇质数)
原根的判定定理
设\(m>1\),g为正整数且\((g,m)=1\)。则g是m的原根当且仅当对于任意\(\phi(m)\)的质因子\(q_i\),\(g^{\frac{\phi(m)}{q_i}}\not\equiv 1(mod \: m)\)
具体实现求原根
首先找到n的最小原根g,则n 的所有原根可以表示为\(g^k\:mod\: n 且gcd(k,\phi(n))=1\)。
我们在得到n的最小原根g后便可在\(O(\phi(n)log\phi(n))\)的时间复杂度内得到n的所有原根。
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<map>
#define ll long long
using namespace std;
const int maxn=1e6+10101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
int x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int tot,is[maxn],prime[maxn],phi[maxn];
void getprime(){
phi[1]=1;
for(int i=2;i<=1000000;i++){
if(!is[i])prime[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot && prime[j]<=1000000/i;j++){
is[prime[j]*i]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
ll power(ll x,ll y,ll mod){
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;
y>>=1;x=x*x%mod;
}
return ans%mod;
}
int gcd(int x,int y){
if(y==0)return x;
return gcd(y,x%y);
}
int t,n,d;
int main(){
getprime();t=read();
while(t--){
n=read();d=read();
vector<int>a,ans;
for(int i=1;i<=tot && prime[i]<=n;i++){
if(phi[n]%prime[i]==0){a.push_back(prime[i]);}
}
int minn=-1;
for(int i=1;i<n;i++){
if(gcd(i,n)!=1)continue;
bool fa=true;
for(auto j:a){
if(power(i,phi[n]/j,n)==1){fa=!fa;break;}
}
if(fa){minn=i;break;}
}
for(int i=1;i<=phi[n] && minn!=-1;i++){
if(gcd(i,phi[n])==1)ans.push_back(power(minn,i,n));
}
sort(ans.begin(),ans.end());
printf("%lu\n",ans.size());
for(int i=1;i*d<=ans.size();i++)printf("%d ",ans[i*d-1]);
printf("\n");
}
return 0;
}
指标
对于质数p,假设g是p的一个原根,则\(g^0,g^1,····,g^{p-2}\)在模p意义下是1,2,···,p-1的一个排列。
假设对于\(1\leq x\leq p-1 有 g^c\equiv x (mod \: p)\),则称x的指标为c,记作\(ind(x)=c\)
性质
\(\forall 1\leq x,y<p, ind(xy)\equiv ind(x)+ind(y) (mod \:\phi(p))\)
\(ind(x^c)\equiv c\cdot ind(x) (mod\: \phi(p))\)
类似于log
求指标--baby step giant step (BSGS)
求\(g^c\equiv x(mod\: p)\)
设\(c=aB-b,当B=\lfloor \sqrt p \rfloor时最优\)
\(g^{aB-b}\equiv x(mod\:p),(0\leq a,b<B)\)
\((g^B)^a\equiv x\cdot g^b (mod \: p)\)
我们可以分别求出当b=0,1,2,····,B-1时的\(x\cdot g^b\)用map存起来
这样分别对于a=0,1,2,···,B-1,直接查找是否存在右边的值等于左边,记录答案即可
例题Discrete Roots题解