51nod1135——原根

题目链接:https://vjudge.net/contest/332708#problem/R

题目:

设m是正整数,a是整数,若a模m的阶等于φ(m),则称a为模m的一个原根。(其中φ(m)表示m的欧拉函数)

给出1个质数P,找出P最小的原根。

Input

输入1个质数P(3 <= P <= 10^9)

Output

输出P最小的原根。

Sample Input

3

Sample Output

2

题解:

做这个题首先要知道原根的定义(不懂请参考这篇博文:原根

前面的一大堆定义可以看也可以不看(如果不懂的话,比如我),我们只需要记住一句话就行了。

如果g是P的原根,就是g^(P-1) = 1 (mod P)当且仅当指数为P-1的时候成立.(这里P是素数)。

然后就是怎么求解的问题了,策略有两种:暴力枚举和有规律的枚举。

当然暴力枚举的方法很简单,我们只需要从2开始,暴力枚举g并判断g^(P-1) = 1 (mod P)是否当且仅当指数为P-1的时候成立。

其实这样速度也不慢,因为原根通常不大。

不过既然我们有优化版本的,我们肯定要用最好的,这里我们需要知道裴蜀定理和欧拉定理,具体内容和证明参考上面给的博文。

我就直接讲怎么去求。
求一个质数的一个原根,先枚举g。

然后我们可以求出x-1的所有质因子,然后去枚举每个质因子p1,p2,p3...pn,判断是否满足g^((x-1)/pi)==1(mod p)。

若满足则是原根,否则不是(证明请参考上面的链接)。

 

那么我们这个题就可以先预处理求出范围内所有的质因子,之后再枚举g.

然后枚举质因子判断是否是g的质因子,之后就和上面的步骤一样了。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define rp(i, s, t) for (i = s; i <= t; i++)
#define RP(i, s, t) for (i = t; i >= s; i--)
#define ll long long
#define ull unsigned long long
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
inline void write(int n)
{
    if (n < 0)
    {
        putchar('-');
        n = -n;
    }
    if (n >= 10)
        write(n / 10);
    putchar(n % 10 + '0');
}
const int N = 1e5 + 7;
int prime[N];
bool visited[N];
int tot;
int p;
void init()//线性筛
{
    memset(visited, false, sizeof visited);
    tot = 0;
    int i, j;
    rp(i, 2, N - 1)
    {
        if (!visited[i])
        {
            prime[tot++] = i;
            visited[i] = true;
        }
        rp(j, 0, tot - 1)
        {
            if (i * prime[j] > N)
                break;
            visited[i * prime[j]] = true;
            if (i % prime[j] == 0)
                break;
        }
    }
}
ll quick_mod(ll a, ll b)//快速幂
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % p;
        b >>= 1;
        a = (a * a) % p;
    }
    return res;
}
bool judge(int x)
{
    int i;
    int pri[N];
    int prime_num = 0;
    int k = p - 1;
    rp(i, 0, tot - 1)//先求出k的所有质因子
    {
        if (k % prime[i] == 0)
        {
            pri[prime_num++] = prime[i];
            while (k % prime[i] == 0)
                k /= prime[i];
        }
        if (k == 1)
            break;
        if (k / prime[i] < prime[i])
        {
            pri[prime_num++] = prime[i];
            break;
        }
    }
    rp(i, 0, prime_num - 1)//枚举质因子并判断
    {
        if (quick_mod(x, (p - 1) / pri[i]) == 1)
        {
            return false;
        }
    }
    return true;
}
int main()
{
    init();
    p = read();
    int i = 2;
    while (1)
    {
        if (judge(i))
            break;
        i++;
    }
    write(i);
    return 0;
}

 


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值