知识点 - 原根
解决问题类型:
- 求原根(最小的/全部的)
- 高次同余方程要用
- NTT要用
定义与结论
定理6.2.1
(
Z
p
∗
,
⋅
)
(\mathbb{Z}_p^*,\cdot)
(Zp∗,⋅) 是循环群,即存在
a
∈
Z
p
∗
a\in \mathbb{Z}_p^*
a∈Zp∗ ,使得
Z
p
∗
=
{
a
n
∣
n
=
1
,
2
,
…
,
p
−
1
}
\mathbb{Z}_p^*=\{a^n|n=1,2,\dots,p-1\}
Zp∗={an∣n=1,2,…,p−1}
这样的
a
a
a 称为
p
p
p 的原根。
性质
- 素数一定有原根(貌似duanLS口误),原根不唯一,部分合数也有原根(对非素数n来说,它的原根 g g g可以让 g k g^k%n gk 产生所有与 n n n互质的数)
- 原根一般不大,暴力枚举即可
- n = 1 , 2 , 4 , p k , 2 p k n=1,2,4,p^k,2p^k n=1,2,4,pk,2pk.时才有
- n n n为质数时, g k g^k%n gk取遍 [ 1 , n − 1 ] [1,n-1] [1,n−1]
- ϕ ( n ) \phi(n) ϕ(n)是最小的使 g ϕ ( n ) ≡ 1 ( m o d n ) g^{\phi (n)} \equiv 1 \pmod n gϕ(n)≡1(modn) 数当且仅当g是n的原根
- ϕ ( ϕ ( n ) ) \phi (\phi (n) ) ϕ(ϕ(n))为n的原根数量
- g g g is O ( log 6 p ) O(\log^6 p) O(log6p).
- 1000000007的原根为5,998244353的原根为3
- 如果g为n的原根,则 g d g^d gd为n的原根的充要条件是 ( d , φ ( n ) ) = 1 (d,φ(n))=1 (d,φ(n))=1(例题用到的结论)
复杂度(找原根模板):
我们先分解 ϕ ( n ) = p 1 a 1 ⋯ p s a s \phi (n) = p_1 ^ {a_1} \cdots p_s ^ {a_s} ϕ(n)=p1a1⋯psas,如果所有 g ϕ ( n ) p i ( m o d n ) g ^ { \frac {\phi (n)} {p_i}} \pmod n gpiϕ(n)(modn)都不等于1,那么 g g g就是原根
O ( A n s ⋅ log ϕ ( n ) ⋅ log n ) O(Ans \cdot \log \phi (n) \cdot \log n) O(Ans⋅logϕ(n)⋅logn), A n s = g Ans=g Ans=g 的数量级是 O ( log 6 p ) O(\log^6 p) O(log6p).原根一般不大,暴力枚举即可
int getPrimitiveRoot(int p) {
vector<int> fact;
int phi = p-1, n = phi;
for (int i=2; i*i<=n; ++i)
if (n % i == 0) {
fact.push_back (i);
while (n % i == 0)
n /= i;
}
if (n > 1)
fact.push_back (n);
for (int res=2; res<=p; ++res) {
bool ok = true;
for (size_t i=0; i<fact.size() && ok; ++i)
ok &= kpow (res, phi / fact[i], p) != 1;
if (ok) return res;
}
return -1;
}
例题
代码
//https://www.cnblogs.com/fenghaoran/p/7110296.html
inline void prepare()
{
phi[1]=1;
for(int i=2;i<N;++i){
if(!vis[i])P[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot;++j){
if(i*P[j]>=N)break;
vis[i*P[j]]=1;
if(i%P[j])phi[i*P[j]]=phi[i]*phi[P[j]];
else{phi[i*P[j]]=phi[i]*P[j];break;}
}
}
}
inline int QPow(int d,int z,int Mod)
{
int ans=1;
for(;z;z>>=1,d=1ll*d*d%Mod)if(z&1)ans=1ll*ans*d%Mod;
return ans;
}
inline bool check(int x)
{
if(x==2 || x==4)return 1;
if((x&1)^1)x>>=1;
for(int i=2;P[i]<=x;++i)
if(x%P[i]==0){
while(x%P[i]==0)x/=P[i];
return x==1?P[i]:0;
}
return 0;
}
inline int get_rg(int fx)
{
int pt[1010],tt=0,Txd=phi[fx];
for(int i=1;P[i]*P[i]<=Txd;++i)
if(Txd%P[i]==0){
pt[++tt]=P[i];
while(Txd%P[i]==0)Txd/=P[i];
}
if(Txd!=1)pt[++tt]=Txd;
for(int i=2;i<=fx;++i)
if(QPow(i,phi[fx],fx)==1){
int flag=1;
for(int j=1;j<=tt;++j)
if(QPow(i,phi[fx]/pt[j],fx)==1){
flag=0;break;
}
if(flag)return i;
}
return 0;
}
inline void work(int fx)
{
int tt=0,pr[N];
if(fx==2){printf("1\n");return;}
if(fx==4){printf("3\n");return;}
int T=check(fx);
if(!T){printf("-1\n");return;}
int g=get_rg(fx);
for(int i=1,k=g;i<phi[fx];++i,k=1ll*k*g%fx)
if(gcd(i,phi[fx])==1)
pr[++tt]=k;
sort(pr+1,pr+tt+1);
for(int i=1;i<tt;++i)
printf("%d ",pr[i]);
printf("%d",pr[tt]);
printf("\n");
}
int main()
{
prepare();
while(scanf("%d",&n)!=EOF)work(n);
return 0;
}