P11961 [GESP202503 五级] 原根判断
题目背景
截止 2025 年 3 月,本题可能超出了 GESP 考纲范围。在该时间点下,原根是 NOI 大纲 8 级知识点(NOI 级),而相对简单的无需原根知识的做法中,使用的费马小定理与欧拉定理也属于 NOI 大纲 7 级知识点(提高级),且均未写明于 GESP 大纲中。需要注意,GESP 大纲和 NOI 大纲是不同的大纲。
若对题目中原根这一概念感兴趣,可以学习完成 【模板】原根。
题目描述
小 A 知道,对于质数 p p p 而言, p p p 的原根 g g g 是满足以下条件的正整数:
- 1 < g < p 1<g<p 1<g<p;
- g p − 1 m o d p = 1 g^{p-1}\bmod{p}=1 gp−1modp=1;
- 对于任意 1 ≤ i < p − 1 1\le i<p-1 1≤i<p−1 均有 g i m o d p ≠ 1 g^i\bmod{p}\neq1 gimodp=1。
其中 a m o d p a\bmod{p} amodp 表示 a a a 除以 p p p 的余数。
小 A 现在有一个整数 a a a,请你帮他判断 a a a 是不是 p p p 的原根。
输入格式
第一行,一个正整数 T T T,表示测试数据组数。
每组测试数据包含一行,两个正整数 a , p a,p a,p。
输出格式
对于每组测试数据,输出一行,如果
a
a
a 是
p
p
p 的原根则输出 Yes
,否则输出 No
。
输入输出样例 #1
输入 #1
3
3 998244353
5 998244353
7 998244353
输出 #1
Yes
Yes
No
说明/提示
数据范围
对于 40 % 40\% 40% 的测试点,保证 3 ≤ p ≤ 1 0 3 3\le p\le10^3 3≤p≤103。
对于所有测试点,保证 1 ≤ T ≤ 20 1\le T\le20 1≤T≤20, 3 ≤ p ≤ 1 0 9 3\le p\le10^9 3≤p≤109, 1 < a < p 1<a<p 1<a<p, p p p 为质数。
解析
感谢 冯世龙老师 提供的题解,可以在不学习原根的情况下解题,详见代码:
#include <iostream>
using namespace std;
// 已知
int t, a, p;
// 未知
int fpq(int a, int b)
{
if (b == 0) return 1;
int res = fpq(a, b / 2); // 快速幂
res = 1L * res * res % p; // 取模
if (b & 1) res = 1L * res * a % p; // b如果是奇数再取模一次
return res;
}
bool check()
{
if (fpq(a, p - 1) != 1) return false; // 判断性质2
// 然后去判断性质3,在没达到p之前找是否有一个数使得a的(p-1)/i次方模p等于1
// 如果有,那么a不是原根,否则是原根,这是基于a^x=(a^p1)^p2,p1*p2=p-1
int phi = p - 1, r = phi; // 利用快速幂分解质因数
for (int i = 2; i * i <= p; i++)
{
if (phi % i == 0) // 找phi的因子,所有因子都会找到
{
// 在p-1之前就有一个数可以使得a的phi次方模p等于1,判断性质3
if (fpq(a, phi / i) == 1) return false;
while (r % i == 0) r /= i; // 对r分解质因数
}
}
// 还有其他质因子 就得再判断一次
if (r != 1 and fpq(a, r) == 1) return false; // 判断性质2
return true;
}
int main()
{
cin >> t;
for (int i = 0; i < t; i++)
{
cin >> a >> p;
if (check()) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
官方标程:
#include <cstdio>
using namespace std;
int a, p;
int ans;
int fpw(int b, int e) {
if (e == 0)
return 1;
int r = fpw(b, e >> 1);
r = 1ll * r * r % p;
if (e & 1)
r = 1ll * r * b % p;
return r;
}
void check(int e) {
if (fpw(a, e) == 1)
ans = 0;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &a, &p);
ans = 1;
int phi = p - 1, r = phi;
for (int i = 2; i * i <= phi; i++)
if (phi % i == 0) {
check(phi / i);
while (r % i == 0)
r /= i;
}
if (r > 1)
check(phi / r);
printf(ans ? "Yes\n" : "No\n");
}
return 0;
}