数论预习笔记-原根
模板题面
预习笔记
wiki
众所周知 OI 不需要证明。
首先原根的定义是:
对于互质的正整数 \(m\) 和整数 \(a\),如果 \(a\mod m\) 的阶等于 \(\varphi (m)\) 的话,就称 \(a\) 为模 \(m\) 的原根。
阶的定义是,对于互质的正整数 \(m\) 和整数 \(a\),满足 \(a^{x} \mod m = 1\) 的最小正整数 \(x\),记作 \(\delta_{m}(a)\)
据王元的证明可知,模 \(m\) 的最小原根不超过 \(m^{0.25}\),所以如果我们暴力枚举找最小原根复杂度也是可以接受的,所以我们找原根的流程就是:
- 判断这个数是否具有原根:
根据原根存在定理:一个数 \(m\) 存在原根当且仅当 \(m = 2,4,p^{\alpha},2p^{\alpha}\),其中 \(p\) 为奇素数,\(\alpha\) 为正整数。
因为 \(m \leq 1e6\),而对于次方能取到最大的 \(3\) 来说不过是 12 次计算,因此我们完全可以预处理出所有素数满足某次方或某次方的 2 倍在这个范围内的值,然后就可以 O(1) 判断数 \(m\) 是否有原根。
反之,如果我们不这样判断的话,我们在暴力枚举的时候就要枚举 \(m - 1\) 次才发现它没有原根还要加上快速幂每次的 log。 - 从小到大枚举,找到 \(a^{\varphi (m)} \mod m = 1\) 的 \(a\), 判断是否合法。
根据原根判定定理:若一个数 \(g\) 是模 \(m\) 的原根,则有对于 \(\varphi (m)\) 任何大于 1 且不为自身的因数 \(p\),都有 \(g^{\varphi (m) / p} \mod m \neq 1\) 。
因此我们只需要花 \(\sqrt m\) 的时间复杂度,求出 \(\varphi (m)\) 的除 1 以外的正因数,再依次判断就行了(质数要包含本身)。 - 找到原根以后再根据一个神奇的定理找出其它的原根
注意除了预处理所有的运算都是在模 \(m\) 意义下的。
对于正整数 \(x\),模 \(m\) 的某原根 \(g\),如果 \(gcd(x,\varphi(m))= 1\),那么 \(g^x\) 也是模 \(m\) 的一个原根,因此我们再枚举这个 \(x\) 就好了,根据上面的定义其中 \(x\) 是小于 \(\varphi (m)\) 的正整数。
以上就是找原根的暴力流程。
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int m[15], d[15], maxn, t, ans[N], cnt = 0;
int Prime[N], v[N], tot;
void GetPrime(int n) {
memset(v, 0, sizeof(v)); tot = 0;
for(int i = 2; i <= n; i++) {
if(v[i] == 0) {
v[i] = i; Prime[++tot] = i;
}
for(int j = 1; j <= tot; j++) {
if(Prime[j] > v[i] || Prime[j] > n / i) break;
v[i * Prime[j]] = Prime[j];
}
}
}
#define LL long long
LL power(LL a, LL b, LL mod) {
LL res = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) res = res * a % mod;
return res;
}
bool is[N];
void init() {
is[2] = is[4] = true; LL x = 1;
for(int i = 2; i <= tot; i++, x = 1)
for(int j = 1; ; j++) {
x = x * Prime[i];
if(x > maxn) break;
is[x] = true;
if(2 * x <= maxn)
is[2 * x] = true;
}
}
int phi(int M) {
int res = M;
for(int i = 2; i * i <= M; i++)
if(M % i == 0) {
res = res / i * (i - 1);
while(M % i == 0) M /= i;
}
if(M > 1) res = res / M * (M - 1);
return res;
}
int fac[N], num = 0;
void divide(int n) {
num = 0;
for(int i = 2; i * i <= n; i++)
if(n % i == 0) {
fac[++num] = i;
if(i != n / i) fac[++num] = n / i;
}
if(n > 1) fac[++num] = n;
}
bool check(int x, int PhiM, int M) {
for(int i = 1; i <= num; i++)
if(power(x, PhiM / fac[i], M) == 1) return false;
return true;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
inline int read() {
int x = 0, f = 1, c = getchar();
for(; !isdigit(c); c = getchar())
if(c == '-')
f = -1;
for(; isdigit(c); c = getchar())
x = x * 10 + c - 48;
return x * f;
}
int main() {
t = read();
for(int i = 1; i <= t; i++)
m[i] = read(), maxn = max(maxn, m[i]),
d[i] = read();
GetPrime(maxn); init();
for(int i = 1; i <= t; i++, cnt = 0) {
if(!is[m[i]]) {
printf("0\n \n"); continue;
}
else {
int PhiM = phi(m[i]), g;
divide(PhiM);
for(int j = 1; j < m[i]; j++)
if(power(j, PhiM, m[i]) == 1 && check(j, PhiM, m[i])) {
g = j; break;
}
int x = 1;
for(int j = 1; j <= PhiM; j++) {
x = 1ll * x * g % m[i];
if(gcd(j, PhiM) == 1) ans[++cnt] = x;
} printf("%d\n", cnt);
sort(ans + 1, ans + 1 + cnt);
for(int j = d[i]; j <= cnt; j += d[i])
printf("%d ", ans[j]);
puts("");
}
}
return 0;
}

浙公网安备 33010602011771号