HDU 4992 求一个数的所有原根

这是一篇关于求解正整数n的所有原根的博客。内容涉及到原根的定义,包括一个数存在原根的充要条件,以及如何找到最小原根并枚举其他合法原根的方法。文章中还特别提到了在处理合数时需要考虑的特殊情况,并给出了相关的ACM竞赛题目链接和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


题意:给一个正整数 n ( n > = 2 ) n(n>=2) n(n>=2),求她的所有原根,若不存在输出 − 1 -1 1


思路:
定义1:一个数 m m m存在原根的充要条件为: m = 2 , 4 , p a , 2 ∗ p a m=2,4,p^a,2*p^a m=2,4,pa,2pa( p p p为奇素数)
可以特判输出 − 1 -1 1的情况。

又由定义2:若 G G G n n n的原根,则当 g c d ( i , φ ( n ) ) = = 1 gcd(i,\varphi(n)) == 1 gcd(i,φ(n))==1 G i G^i Gi也为 n n n的原根
可以通过先求出 n n n的最小原根,来枚举得到其他的合法原根。

对于求一个数的最小原根就是一个很基础的问题了,此处需要注意的是 n n n可能是合数,则 a φ ( n ) ≡ 1 ( m o d   n ) a^{\varphi(n)} \equiv 1 (mod \ n) aφ(n)1(mod n) g c d ( a , n ) ≠ 1 gcd(a,n) \neq 1 gcd(a,n)̸=1不成立,故需要提前特判。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int A = 1e6 + 10;
int pri[A],phi[A],ans[A],q[A],n,tot;
bool vis[A];

int gcd(int a,int b){ return b?gcd(b,a%b):a;}
void init(){
    tot = 0;phi[1] = 1;
    for(int i=2 ;i<A ;i++){
        if(!vis[i]){pri[++tot] = i;phi[i] = i-1;}
        for(int j=1 ;j<=tot&&i*pri[j]<A ;j++){
            vis[i*pri[j]] = 1;
            if(i%pri[j] == 0){
                phi[i*pri[j]] = pri[j] * phi[i];break;
            }
            phi[i*pri[j]] = phi[pri[j]] * phi[i];
        }
    }
}

ll fast_pow(ll a,ll b){
    ll res = 1;
    while(b){
        if(b&1) res = res*a%n;
        a = a*a%n;
        b >>= 1;
    }
    return res;
}

int get_G(){
    int t = 0,tem = phi[n];
    for(int i=1 ;i<=tot && pri[i]*pri[i]<=tem ;i++){
        if(tem%pri[i] == 0){
            q[++t] = pri[i];
            while(tem%pri[i]==0) tem/=pri[i];
        }
    }
    if(tem>1) q[++t] = tem;
    for(int i=2;i<n;i++){
        if(gcd(i,n) != 1) continue;
        bool flag = 0;
        for(int j=1 ;j<=t ;j++) if(fast_pow(i,phi[n]/q[j]) == 1) {flag=1;break;}
        if(!flag) return i;
    }
    return -1;
}

bool check(int n){
    if(n==2 || n==4) return true;
    if(n%2 == 0)     n >>= 1;
    for(int i=2 ;pri[i]<=n ;i++){
        if(n%pri[i] == 0){
            while(n%pri[i] == 0)n/=pri[i];
            return n==1?true:false;
        }
    }
    return false;
}

void solve(){
    if(!check(n)) {puts("-1");return;}
    if(n == 2)    {puts("1");return;}
    int G = get_G();
    int twt = 0;
    for(int i=1,k=G ;i<phi[n] ;i++){
        if(gcd(i,phi[n]) == 1){
            ans[++twt] = k;
        }
        k = 1LL*k*G%n;
    }
    sort(ans+1,ans+1+twt);
    for(int i=1 ;i<=twt ;i++) printf("%d%c",ans[i],i==twt?'\n':' ');
}

int main(){
    init();
    while(~scanf("%d",&n)){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值