设 m 的一个原根为 g,那么 g,g2,…,gφ(m) 包含了 m 的所有原根,并且这些原根满足:
δm(gk)=φ(m)⟺k⊥φ(m)
这样的 k 有 φ(φ(m)) 个,就是 m 的原根个数。
判定原根定理
在前面讲的阶的性质四中,如果 ak≡1(modm),那么 δm(a)∣k,又因为原根要求 δm(a)=φ(m)。因此我们找到每一个 φ(m) 的质因子 d1,d2,…,dt 即可,如果存在 dk 满足 adkφ(m)≡1(modm),则说明 a 不是 m 的原根,它的阶小于 φ(m)。否则 a 为 m 的原根。
那么如何枚举 a 呢?答案是暴力。目前被证明了的其中一个存在原根的数 m 的最小原根上界为 m41+ϵ,可以看见这是非常小的,因此我们考虑暴力的方式,从而得到一个数的最小原根,并推出其它的所有原根。
inlinevoidInit() { phi[1] = 1; for (int i = 2; i < N; i ++) { if (!vis[i]) primes[++ cnt] = i, phi[i] = i - 1; for (int j = 1; i * primes[j] < N; j ++) { vis[i * primes[j]] = true; if (i % primes[j] == 0) { phi[i * primes[j]] = phi[i] * primes[j]; break; } else phi[i * primes[j]] = phi[i] * (primes[j] - 1); } }
rt[2] = rt[4] = true; // 依据原根的存在原理 for (int i = 2; i <= cnt; i ++) { for (int j = 1; primes[i] * j < N; j *= primes[i]) rt[j * primes[i]] = true; for (int j = 2; primes[i] * j < N; j *= primes[i]) rt[j * primes[i]] = true; } }
int n, d; int divs[N], tot;
inlinevoiddivide(int x) { tot = 0; for (int i = 2; i <= x / i; i ++) if (x % i == 0) { divs[++ tot] = i; while (x / i == 0) x /= i; } if (x > 1) divs[++ tot] = x; }
inlineintksm(int base, int k, int mod) { int res = 1; while (k) { if (k & 1) res = res * base % mod; base = base * base % mod, k >>= 1; } return res; }
inlineboolcheck(int cur, int p)// 依据原根判定定理 { if (ksm(cur, phi[p], p) != 1) returnfalse; for (int i = 1; i <= tot; i ++) if (ksm(cur, phi[p] / divs[i], p) == 1) returnfalse; returntrue; }
inlineintfind(int p)// 找到 p 的最小原根 { for (int cur = 1; cur < p; cur ++) if (check(cur, p)) return cur; return0; }
voidsolve() { read(n, d); if (!rt[n]) returnvoid(puts("0\n"));
divide(phi[n]); int minrt = find(n);
vector<int> ans; for (int cur = 1, prod = 1; cur <= phi[n]; cur ++) // 所有其他原根都是最小原根的若干幂 { prod = prod * minrt % n; if (__gcd(cur, phi[n]) == 1) ans.emplace_back(prod); } sort(begin(ans), end(ans)); cout << ans.size() << '\n'; for (int i = 0; i < ans.size(); i ++) if ((i + 1) % d == 0) cout << ans[i] << ' '; cout << '\n'; }