洛谷P11961 [GESP202503 五级] 原根判断

传送门

原题

题目背景

截止 2025 年 3 月,本题可能超出了 GESP 考纲范围。在该时间点下,原根是 NOI 大纲 8 级知识点(NOI 级),而相对简单的无需原根知识的做法中,使用的费马小定理与欧拉定理也属于 NOI 大纲 7 级知识点(提高级),且均未写明于 GESP 大纲中。需要注意,GESP 大纲和 NOI 大纲是不同的大纲。

若对题目中原根这一概念感兴趣,可以学习完成 【模板】原根

题目描述

小 A 知道,对于质数 \(p\) 而言,\(p\) 的原根 \(g\) 是满足以下条件的正整数:

  • \(1<g<p\)
  • \(g^{p-1}\bmod{p}=1\)
  • 对于任意 \(1\le i<p-1\) 均有 \(g^i\bmod{p}\neq1\)

其中 \(a\bmod{p}\) 表示 \(a\) 除以 \(p\) 的余数。

小 A 现在有一个整数 \(a\),请你帮他判断 \(a\) 是不是 \(p\) 的原根。

输入格式

第一行,一个正整数 \(T\),表示测试数据组数。

每组测试数据包含一行,两个正整数 \(a,p\)

输出格式

对于每组测试数据,输出一行,如果 \(a\)\(p\) 的原根则输出 Yes,否则输出 No

输入输出样例 #1

输入 #1

3
3 998244353
5 998244353
7 998244353

输出 #1

Yes
Yes
No

说明/提示

数据范围

对于 \(40\%\) 的测试点,保证 \(3\le p\le10^3\)

对于所有测试点,保证 \(1\le T\le20\)\(3\le p\le10^9\)\(1<a<p\)\(p\) 为质数。

整理&思路

十分明显,这是一道原根题目。首先我们需要写一个快速幂,以备不时之需:

int fpow(int a, int b, int p) {
    int res = 1;
    while(b) {
        if(b&1) res = res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}

很明显,这是一个分治思想。当 \(b\) 为奇数时,将 \(a\) 乘到答案中,然后 \(a\) 平方,\(b\) 右移一位。当 \(b\) 为偶数时,\(a\) 平方,\(b\) 右移一位。这样,我们就可以快速求出 \(a^b\bmod{p}\)

然后我们需要做一个获取因数的方法:

vector<int> fac;
for(int i=2;i<=p;i++) {
    if(p%i==0) {
        fac.push_back(i);
        if(i*i!=p) fac.push_back(p/i);
    }
}

此时我们就可以通过分解(p-1)的方式获取因数,进而通过快速幂以此证明原根(因为如果因数的%是1,那么原根一定不是它,反之亦然 )。

然而,很明显的,我们所采取的是\(p\)的因数而不是\(p-1\)的因数,所以我们需要这样一个骚操作

p--;
/*...*/
p++;

完美awa

接下来就是完整代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;

int T;

// p的原根是a,则:
// 1 < a < p
// a^(p-1) mod p = 1
// 对于任意1 <= i < p-1均有a^i mod p != 1

// 快速幂
int fpow(int a, int b, int p) {
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

signed main() {
    scanf("%lld", &T);
    while (T--) {
        int a, p;
        scanf("%lld%lld", &a, &p);
        vector<int> fac;
        p--;
        for(int i=2;i*i<=p;i++) {
            if(p%i==0) {
                fac.push_back(i);
                if(i*i!=p) fac.push_back(p/i);
            }
        }
        p++;
        if(fpow(a, p-1, p) != 1)
            goto end;
        for(auto x : fac) {
            if(fpow(a, x, p) == 1)
                goto end;
        }
        puts("Yes");
        continue;
        end:
        puts("No");
    }
    return 0;
}

恭喜AC!

posted @ 2025-09-28 16:24  Kibrel  阅读(10)  评论(0)    收藏  举报