HDU 4992 Primitive Roots原根

彩蛋
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} 2pn(其中p为奇素数,n为正整数)
利用该条件判断出n是否存在原根
原根的求解只能暴力去求(不可以直接求出原根,但可以枚举判断该数字是否是原根),但是可以根据条件:
若g为模m的原根,当(i, φ ( m ) \varphi \left ( m \right ) φ(m))=1 时 g i g^i gi也为模m的原根
既然这样就可以求出最小的原根暴力枚举幂数(为防止暴力枚举判断数字是否是原根而导致超时)
所以该问题就转化为了求最小原根。

  1. 求出 φ ( m ) \varphi \left ( m \right ) φ(m)(一般求质数的原根时求出的是m-1但其实也是求出了 φ ( m ) \varphi \left ( m \right ) φ(m)

  2. 因为 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就不是原根。

  3. 快速幂的运用,素数筛的运用 ,欧拉函数的运用

#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))来计算

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值