题目链接: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;
}