彩蛋
Problem Description
We say that integer x, 0 < x < n, is a primitive root modulo n if and only if the minimum positive integer y which makes xy = 1 (mod n) true is φ(n) .Here φ(n) is an arithmetic function that counts the totatives of n, that is, the positive integers less than or equal to n that are relatively prime to n. Write a program which given any positive integer n( 2 <= n < 1000000) outputs all primitive roots of n in ascending order.
Input
Multi test cases.
Each line of the input contains a positive integer n. Input is terminated by the end-of-file seperator.
Output
For each n, outputs all primitive roots of n in ascending order in a single line, if there is no primitive root for n just print -1 in a single line.
Sample Input
4
25
Sample Output
3
2 3 8 12 13 17 22 23
题意:给出一个数字n求出所有的原根,没有输出-1;
模m有原根的充要条件:m=2,4,
p
n
p^{n}
pn,
2
∗
p
n
2*p^{n}
2∗pn(其中p为奇素数,n为正整数)
利用该条件判断出n是否存在原根
原根的求解只能暴力去求(不可以直接求出原根,但可以枚举判断该数字是否是原根),但是可以根据条件:
若g为模m的原根,当(i,
φ
(
m
)
\varphi \left ( m \right )
φ(m))=1 时
g
i
g^i
gi也为模m的原根
既然这样就可以求出最小的原根暴力枚举幂数(为防止暴力枚举判断数字是否是原根而导致超时)
所以该问题就转化为了求最小原根。
-
求出 φ ( m ) \varphi \left ( m \right ) φ(m)(一般求质数的原根时求出的是m-1但其实也是求出了 φ ( m ) \varphi \left ( m \right ) φ(m)
-
因为 g φ ( m ) ≡ 1 ( m o d m ) g^{\varphi \left ( m \right )}\equiv 1\left ( mod m \right ) gφ(m)≡1(modm),而对于比 φ ( m ) \varphi \left ( m \right ) φ(m)小的数都不成立。枚举 φ ( m ) \varphi \left ( m \right ) φ(m)的质因子p,看 g φ ( m ) / p g^{\varphi \left ( m \right )}/p gφ(m)/p在模意义下是否是1。是1的话g就不是原根。
-
快速幂的运用,素数筛的运用 ,欧拉函数的运用
#include<bits/stdc++.h>
using namespace std;
const int Max=1e6+10;
bool P[Max];
int pp[Max];
int phi(int n){
int ans=n;
for(int i=2;i<=n;i++){
if(n%i==0) ans-=ans/i;
while(n%i==0) n/=i;
}
if(n>1) ans-=ans/n;
return ans;
}
void ini(){
memset(P,true,sizeof(P));
P[0]=P[1]=false;
pp[0]=0;
for(int i=2;i<Max;i++){
if(P[i]) pp[++pp[0]]=i;
for(int j=1;j<=pp[0]&&i*pp[j]<Max;j++){
P[i*pp[j]]=false;
if(i%pp[j]==0) break;
}
}
}
int poww(int a,int b,int mod ){
long long ans=1,base=a%mod;
while(b){
if(b&1) ans=(ans%mod*(base%mod))%mod;
base=(base%mod*(base%mod))%mod;
b>>=1;
}
return (int)ans;
}
bool judge(int n){
if(P[n]) return true;
for(int i=2;i<=sqrt(n);i++)
if(n%i==0){
while(n%i==0) n/=i;
return n==1;
}
return false;
}
vector<int>p;
void getp(int n){
p.clear();
for(int i=1;i<=pp[0];i++){
if(n%pp[i]==0) p.push_back(pp[i]);
while(n%pp[i]==0) n/=pp[i];
if(n==1) return;
}
if(n>1) p.push_back(n);
}
void getG(int n){
vector<int>g;
int Phi=phi(n);
getp(Phi);
bool flag=false;
for(int G=2;G<n;G++){
bool falg=true;
for(int j=0;j<p.size();j++){
if(poww(G,Phi/p[j],n)==1){
falg=false;
break;
}
}
if(falg&&__gcd(G,n)==1){
flag=true;
g.push_back(G);
break;
}
}
if(!flag){
cout<<-1<<endl;
return;
}
for(int i=2;i<Phi;i++){
if(__gcd(i,Phi)==1) g.push_back(poww(g[0],i,n));
}
sort(g.begin(),g.end());
g.erase(unique(g.begin(),g.end()),g.end());
for(int i=0;i<g.size();i++){
if(i!=0) cout<<" ";
cout<<g[i];
}
cout<<endl;
}
void solve(int n){
if(n==1) {
cout<<0<<endl;
return;
}
if(n==2){
cout<<1<<endl;
return;
}
if(n==4){
cout<<3<<endl;
return;
}
int t=n;
if(n%2==0) n>>=1;
if(!judge(n)){
cout<<-1<<endl;
return;
}
getG(t);
}
int main(){
ios::sync_with_stdio(false);
ini();
int n;
while(cin>>n){
solve(n);
}
}
p.s. 求模m原根个数利用公式 φ ( φ ( m ) ) \varphi \left ( \varphi \left ( m \right ) \right ) φ(φ(m))来计算