洛谷传送门
SPOJ传送门
题目描述
给你一个质数 p p p以及 n n n组询问, 判定给定的 r r r是否为 p p p的原根。
输入输出格式
输入格式
题目有多组测试数据。
每组测试数据的第一行两个正整数 p , n ( p < 2 31 , 1 ≤ n ≤ 100 ) p,n(p<2^{31},1\le n\le 100) p,n(p<231,1≤n≤100)。
以下 n n n行, 每行一个数表示这组询问中的 r r r。
当 n = p = 0 n=p=0 n=p=0的时候表示输入数据结束, 无需回答。保证数据组数最多 60 60 60组。
输出格式
对于每组询问, 若
r
r
r是
p
p
p的原根, 输出YES
, 否则输出NO
。
解题分析
考虑一个数 g g g如果不是 p p p的原根, 那么显然有 g i ≡ g j ( 1 ≤ i < j ≤ p − 1 ) ( m o d p ) g^i\equiv g^j(1\le i< j\le p-1)(mod\ p) gi≡gj(1≤i<j≤p−1)(mod p), 就有 g j − i ≡ 1 ( m o d p ) g^{j-i}\equiv 1(mod\ p) gj−i≡1(mod p)。又因为 g p − 1 ≡ 1 ( m o d p ) g^{p-1}\equiv 1(mod\ p) gp−1≡1(mod p), 就有 g g c d ( p − 1 , j − i ) ≡ 1 ( m o d p ) g^{gcd(p-1,j-i)}\equiv 1(mod\ p) ggcd(p−1,j−i)≡1(mod p), 也就是说存在 p − 1 p-1 p−1的某个因数 k k k, 使得 g k ≡ 1 ( m o d p ) g^k\equiv 1(mod\ p) gk≡1(mod p), 我们预处理出 p − 1 p-1 p−1的因数判断就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int p, n;
std::vector <int> fac;
IN int fpow(R int base, R int tim)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % p;
base = 1ll * base * base % p, tim >>= 1;
}
return ret;
}
IN void divide()
{
std::vector <int>().swap(fac);
int bd = std::sqrt(p - 1);
for (R int i = 2; i <= bd; ++i)
{
if (!((p - 1) % i))
{
fac.push_back(i);
if (i ^ ((p - 1) / i)) fac.push_back((p - 1) / i);
}
}
}
IN bool check(R int tar)
{
for (auto i : fac) if (fpow(tar, i) == 1) return false;
return true;
}
int main(void)
{
int foo;
W (233)
{
in(p), in(n);
if (!(p | n)) return 0;
divide();
W (n--)
{
in(foo);
if (check(foo)) puts("YES");
else puts("NO");
}
}
}