原根 学习笔记

先看看啥是

由欧拉定理可知,对于 \(a\in\mathbf{Z},m\in\mathbf{N}^*\) ,若 \((a,m)=1\) ,则 \(a^{\varphi(m)}\equiv1\pmod m\)

因此满足同余式 \(a^n\equiv1\pmod m\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\)\(m\) 的阶,记作 \(\delta_m(a)\)\(\operatorname{ord}_m(a)\)

再看看有啥性质。

  1. \(a,a^2,\dots,a^{\delta_m(a)}\)\(m\) 两两不同余。
  2. \(a^n\equiv1\pmod m\) ,则 \(\delta_m(a)|n\)
  3. \(m\in\mathbf{N}^*,a\in\mathbf{Z},b\in\mathbf{Z},(a,m)=(b,m)=1\)
    \(\delta_m(ab)=\delta_m(a)\delta_m(b)\) 的充要条件为 \((\delta_m(a),\delta_m(b))=1\)
点击查看必要性证明

易知 \(a^{\delta_m(a)}\equiv1\pmod m,b^{\delta_m(b)}\equiv1\pmod m\)

\((ab)^{[\delta_m(a),\delta_m(b)]}\equiv1\pmod m\)

可知 \(\delta_m(ab)|[\delta_m(a),\delta_m(b)]\)

又因为 \(\delta_m(ab)=\delta_m(a)\delta_m(b)\)

所以 \(\delta_m(a)\delta_m(b)|[\delta_m(a),\delta_m(b)]\)

所以 \((\delta_m(a),\delta_m(b))=1\)

点击查看充分性证明

易知 \((ab)^{\delta_m(ab)}\equiv1\pmod m\)

\((ab)^{\delta_m(ab)}\equiv(ab)^{\delta_m(ab)\delta_m(b)}\equiv a^{\delta_m(ab)\delta_m(b)}\equiv1\pmod m\)

所以 \(\delta_m(a)|\delta_m(ab)\delta_m(b)\)

又因为 \((\delta_m(a),\delta_m(b))=1\)

所以 \(\delta_m(a)|\delta_m(ab)\)

同理 \(\delta_m(b)|\delta_m(ab)\)

所以 \(\delta_m(a)\delta_m(b)|\delta_m(ab)\)

又因为 \((ab)^{\delta_m(a)\delta_m(b)}\equiv(a^{\delta_m(a)})^{\delta_m(b)}(b^{\delta_m(b)})^{\delta_m(a)}\equiv1\pmod m\)

所以 \(\delta_m(ab)|\delta_m(a)\delta_m(b)\)

综上 \(\delta_m(ab)=\delta_m(a)\delta_m(b)\)

  1. \(k\in\mathbf{N},m\in\mathbf{N}^*,a\in\mathbf{Z},(a,m)=1\) ,则 \(\delta_m(a^k)=\dfrac{\delta_m(a)}{(\delta_m(a),k)}\) .
点击查看证明

注意到 \(a^{k\delta_m(a^k)}\equiv (a^k)^{\delta_m(a^k)}\equiv1\pmod m\)

\(\delta_m(a)|k\delta_m(a^k)\) ,即 \(\dfrac{\delta_m(a)}{(\delta_m(a),k)}|\delta_m(a^k)\)

又注意到 \((a^k)^{\frac{\delta_m(a)}{(\delta_m(a),k)}}\equiv(a^{\delta_m(a)})^{\frac{k}{(\delta_m(a),k)}}\equiv1\pmod m\)

\(\delta_m(a^k)|\dfrac{\delta_m(a)}{(\delta_m(a),k)}\)

综上 \(\delta_m(a^k)=\dfrac{\delta_m(a)}{(\delta_m(a),k)}\)

原根

说完,再看看啥是原根.

\(m\in\mathbf{N}^*,g\in\mathbf{Z}\) 。若 \((g,m)=1\) ,且 \(\delta_m(g)=\varphi(m)\) ,则称 \(g\) 为模 \(m\) 的原根。

原根有什么性质呢?

  1. (原根判定定理)设 \(m\geqslant3,(g,m)=1\) ,则 \(g\) 是模 \(m\) 的原根的充要条件为,对于 \(\varphi(m)\) 的每个素因数 \(p\) ,都有 \(g^{\frac{\varphi(m)}{p}}\not\equiv1\pmod m\)
点击查看充分性证明

使用反证法。

当对于 \(\varphi(m)\) 的每个素因数 \(p\) ,都有 \(g^{\frac{\varphi(m)}{p}}\not\equiv 1\pmod m\) 成立时,我们假设存在一个 \(g\),其不是模 \(m\) 的原根。

因为 \(g\) 不是 \(m\) 的原根,则存在一个 \(t<\varphi(m)\) 使得 \(g^t\equiv 1\pmod{m}\)

由裴蜀定理得,一定存在一组 \(k,x\) 满足 \(kt=x\varphi(m)+(t,\varphi(m))\)

又由欧拉定理得 \(g^{\varphi(m)}\equiv 1\pmod{m}\) ,故有:

\[1\equiv g^{kt}\equiv g^{x\varphi(m)+(t,\varphi(m))}\equiv g^{(t,\varphi(m))}\pmod{m} \]

由于 \((t, \varphi(m)) \mid \varphi(m)\)\((t, \varphi(m))\leqslant t < \varphi(m)\)

故存在 \(\varphi(m)\) 的素因数 \(p\) 使得 \((t, \varphi(m)) \mid \frac{\varphi(m)}{p}\)

\(g^{\frac{\varphi(m)}{p}}\equiv g^{(t, \varphi(m))}\equiv 1\pmod{m}\) ,与条件矛盾。

故假设不成立,原命题成立。

  1. (原根个数)若一个数 \(m\) 有原根,则它原根的个数为 \(\varphi(\varphi(m))\)
点击查看证明

\(m\) 有原根 \(g\) ,则:

\[\delta_m(g^k)=\dfrac{\delta_m(g)}{\left(\delta_m(g),k\right)}=\dfrac{\varphi(m)}{\left(\varphi(m),k\right)} \]

所以若 \(\left(k,\varphi(m)\right)=1\) ,则有: \(\delta_m(g^k)=\varphi(m)\) ,即 \(g^k\) 也是模 \(m\) 的原根。

而满足 \(\left(\varphi(m),k\right)=1\)\(1\leq k \leq \varphi(m)\)\(k\)\(\varphi(\varphi(m))\) 个。所以原根就有 \(\varphi(\varphi(m))\) 个。

  1. (原根存在定理)一个数 \(m\) 存在原根当且仅当 \(m=2,4,p^{\alpha},2p^{\alpha}\) ,其中 \(p\) 为奇素数, \(\alpha\in \mathbf{N}^{*}\)
  1. (最小原根的范围估计)见 OI-Wiki

题目

题单: https://vjudge.net/article/5400

洛谷 P6081 【模板】原根

https://www.luogu.com.cn/article/d1yftjt7

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1000010;
int t,p,cnt,tot,ctans,fc[MAXN],ans[MAXN],pri[MAXN],rt[MAXN],q[MAXN],phi[MAXN];
void init () {
phi[1]=1;
for (int i=2;i<=MAXN-10;i++) {
if (!q[i]) {pri[++tot]=i,phi[i]=i-1;}
for (int j=1;j<=tot&&pri[j]*i<=MAXN-10;j++) {
q[i*pri[j]]=1;
if (i%pri[j]==0) {
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}
rt[2]=rt[4]=1;
for (int i=2;i<=tot;i++) {
for (int j=1;(1ll*j*pri[i])<=MAXN-10;j*=pri[i]) {rt[j*pri[i]]=1;}
for (int j=2;(1ll*j*pri[i])<=MAXN-10;j*=pri[i]) {rt[j*pri[i]]=1;}
}
return;
}
int gcd (int a,int b) {return (b==0?a:gcd(b,a%b));}
int qpow (int a,int b,int p) {
int res=1;
while (b) {
if (b&1) {res=(1ll*res*a)%p;}
a=(1ll*a*a)%p;
b>>=1;
}
return res;
}
void proc (int p) {
for (int i=2;i*i<=p;i++) {
if (p%i==0) {
fc[++cnt]=i;
while (p%i==0) {p/=i;}
}
}
if (p>1) {fc[++cnt]=p;}
return;
}
bool chk (int x,int p) {
if (qpow(x,phi[p],p)!=1) {return 0;}
for (int i=1;i<=cnt;i++) {
if (qpow(x,phi[p]/fc[i],p)==1) {return 0;}
}
return 1;
}
int findrt (int p) {
for (int i=1;i<p;i++) {
if (chk(i,p)) {return i;}
}
return 0;
}
void getrt (int p,int x) {
int prod=1;
for (int i=1;i<=phi[p];i++) {
prod=(1ll*prod*x)%p;
if (gcd(i,phi[p])==1) {
ans[++ctans]=prod;
}
}
return;
}
int main () {
init();
scanf("%d",&t);
for (int ii=1;ii<=t;ii++) {
int wtf;
scanf("%d%d",&p,&wtf);
if (rt[p]) {
ctans=cnt=0;
proc(phi[p]);
int mn=findrt(p);
getrt(p,mn);
sort(ans+1,ans+ctans+1);
printf("%d\n",ctans);
for (int i=1;i<=ctans/wtf;i++) {printf("%d ",ans[i*wtf]);}
printf("\n");
} else {
printf("0\n\n");
}
}
return 0;
}

POJ 1284 Primitive Roots

直接求 \(\varphi(\varphi(p))\) 即可。

#include <iostream>
using namespace std;
typedef long long ll;
const ll N = 65536;
bool np[N + 5];
ll phi[N + 5], pri[N + 5], tot = 0;
void init() {
phi[1] = 1;
for (ll i = 2; i <= N; i++) {
if (!np[i]) {
pri[++tot] = i;
phi[i] = i - 1;
}
for (ll j = 1; i * pri[j] <= N && j <= tot; j++) {
np[i * pri[j]] = true;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
init();
ll x;
while (cin >> x)
cout << phi[phi[x]] << endl;
return 0;
}

CodeForces 615D Multipliers

\(n=\prod_{i} p_i^{r_i}\quad(\forall\lambda,\mu\in[1,m],p_\lambda\ne p_\mu)\)

则答案为:

\[\begin{aligned} ans&=\prod_{i}p_i^{\sum_{j=0}^{r_i}j\prod_{j\ne i}(r_j+1)}\\ &=\prod_{i}p_i^{\frac{r_i(r_i+1)}{2}\prod_{j\ne i}(r_j+1)}\\ &=\prod_{i}p_i^{\frac{r_i}{2}\prod_{j}(r_j+1)} \end{aligned}\]

,直接计算即可,注意要处理好有关 \(\frac{1}{2}\) 的问题。

#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
void read(ll &x) {
char c = getchar();
ll v = 0, f = 1;
while (c < '0' || '9' < c) {
if (c == '-')
f = -1;
c = getchar();
}
while ('0' <= c && c <= '9') {
v = (v << 1) + (v << 3) + (c ^ 48);
c = getchar();
}
x = v * f;
}
const ll mod = 1e9 + 7, mxp = 2 * mod - 2;
ll qpow(ll x, ll y) {
ll z = 1;
while (y) {
if (y & 1)
z = z * x % mod;
x = x * x % mod;
y >>= 1;
}
return z;
}
const ll M = 2e5 + 5;
ll m, p[M], r[M];
bool div2 = false;
ll exp = 1, ans = 1;
int main() {
read(m);
for (ll i = 1; i <= m; i++) {
read(p[i]);
r[p[i]]++;
}
sort(p + 1, p + m + 1);
m = unique(p + 1, p + m + 1) - (p + 1);
for (ll i = 1; i <= m; i++)
exp = exp * (r[p[i]] + 1) % mxp;
for (ll i = 1; i <= m; i++)
ans = ans * qpow(p[i], exp * r[p[i]] / 2) % mod;
printf("%lld\n", ans);
return 0;
}

CodeForces 360D Levko and Sets

注意:下面提到的数默认在 \(\bmod p\) 意义下。
\(\varphi=\varphi(p)=p-1,g=\gcd(\varphi,\gcd_{i=1}^m b_i)\)\(G\)\(p\) 的原根。

则集合 \(i\)\(S_i=\{a_i^{gk}|k\in\mathbf{N}\}\)

可以找到最小的 \(c_i\) 使 \(a_i^{gc_i}\equiv1\)

所以 \(S_i=\{1,a^g,a^{2g},\dots,a^{(c_i-1)g}\}\)

\(S_i=\{G^0,G^{\frac{\varphi}{c_i}},G^{\frac{2\varphi}{c_i}},\dots,G^{\frac{(c_i-1)\varphi}{c_i}}\}\)

\(s_i=\frac{\varphi}{c_i}\) ,问题转化为 \(x\) 满足 \(x\in[1,\varphi],\exists s_i|x\) 的个数。

\(\operatorname{d}(x)\) 为在 \([1,\varphi]\) 中, \(x\) 倍数的个数。

则答案为 \(\sum\operatorname{d}(c_i)-\sum\operatorname{d}(\operatorname{lcm}(c_i,c_{i+1}))+\dots\)

CodeForces 284A Cows and Primitive Roots

直接求 \(\varphi(\varphi(p))\) 即可。

#include <cstdio>
using namespace std;
void read(int &x) {
char c = getchar();
int v = 0, f = 1;
while (c < '0' || '9' < c) {
if (c == '-')
f = -1;
c = getchar();
}
while ('0' <= c && c <= '9') {
v = (v << 1) + (v << 3) + (c ^ 48);
c = getchar();
}
x = v * f;
}
const int N = 2000;
bool np[N + 5];
int phi[N + 5], pri[N + 5], tot = 0;
void init() {
phi[1] = 1;
for (int i = 2; i <= N; i++) {
if (!np[i]) {
pri[++tot] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= tot; j++) {
int k = i * pri[j];
if (k > N)
break;
np[k] = true;
if (i % pri[j] == 0) {
phi[k] = phi[i] * pri[j];
break;
}
phi[k] = phi[i] * (pri[j] - 1);
}
}
}
int p;
int main() {
init();
read(p);
printf("%d\n", phi[phi[p]]);
return 0;
}

AtCoder abc335_g Discrete Logarithm Problems

AtCoder abc212_h Nim Counting

posted @   01bit  阅读(22)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示