基础数论复习

Algorithm

数论,是一门研究整数的纯数学的分支,而整数的基本元素是素数(也称质数),所以数论的本质是对素数性质的研究。

素数

一些基本概念就不写了.

注意两点:

  1. 0、1、负整数既不是素数也不是合数.
  2. π(n)\pi(n) 表示不大于 nn 的素数个数, 有如下近似关系 π(n)nlnn\pi(n)\sim\dfrac{n}{\ln{n}} .

素数判定

如何判定正整数 nn 是否是素数?

很容易想到用每一个小于 nn 且大于 11 的数来判断它是否是 nn 的约数, 若都不是, 则 nn 为素数, 否则为合数.

这个暴力算法是 O(n)\mathcal{O}(n) 的, 但是我们考虑到, 若数 ppnn 的约数, 则 np\frac{n}{p} 也是 nn 的约数, 所以我们只需用 2n2\sim \sqrt{n} 的数来判定即可, 于是优化一下我们得到一个 O(n)\mathcal{O}(\sqrt{n}) 的算法.

1
2
3
4
5
6
7
bool isPrime(int n) {
if (n == 1) return false;
if (n == 2) return true;
for (int i = 2; i * i <= n; ++i)
if (n % i == 0) return false;
return true;
}

整数惟一分解定理

若整数 n2n\ge2 , 那么 nn 一定可以惟一地表示为若干素数的乘积, 形如

n=p1r1p2r2pkrk(pi为素数,ri0)n=p_1^{r_1}p_2^{r_2}\cdots p_k^{r_k}(p_i为素数, r_i\ge0)

1
2
3
4
5
6
7
8
9
10
vector <int> factor(int n) {
vector <int> ret;
for (int i = 2; i * i <= n; ++i)
while (n % i == 0) {
ret.push_back(i);
n /= i;
}
if (n > 1) ret.push_back(n);
return ret;
}

素数筛法

主要是埃氏筛法和欧拉筛法.

Eratosthenes 筛法

Eratosthenes 筛法可以快速列举出给定范围内的所有素数.

一个合数一定可以写成 pxp\cdot x 的形式, 其中 pp 为素数, xx 为整数, 因此对每一个 pp , 我们从小到大枚举 xx , 筛掉相应的 pxp\cdot x 即可.

一点小改进: 当 x<px<p 时, pxp\cdot x 一定被 xx 的素因子筛过一遍, 所以我们枚举 xx 时从 x=px=p 开始枚举.

时间复杂度: O(n2+n3+n5+)=O(nloglogn)\mathcal{O}(\frac{n}{2}+\frac{n}{3}+\frac{n}{5}+\cdots)=\mathcal{O}(n\log{\log{n}})

1
2
3
4
5
6
7
8
9
10
11
bool isprime[N];

void Eratos(int n) {
memset(isprime, 1, sizeof isprime);
isprime[0] = isprime[1] = 0;
for (int i = 2; i * i <= n; ++i)
if (isprime[i]) {
for (int j = i * i; j <= n; j += i)
isprime[j] = 0;
}
}

例题

「POJ 2689」Prime Distance

由于这里 L,RL,R 的范围很大, 暴力筛完之后枚举求解肯定会 T , 但是 RL106R-L\le 10^6 的范围却是在我们的承受范围之内的.

于是我们考虑用 1n1\sim n 这一小范围的素数筛掉 LRL\sim R 之间的合数.

假设数 k[L,R]k\in[L, R] , 其中 kk 的最小质因数为 pp , 满足 p>np>n , 则 k=pxk=p\cdot x , 若 x<px<p , 则 kk 一定存在小于 pp 的质因数, 矛盾, 故而 xpx\ge p , 也就是说, 会被漏筛的数 kk 满足 kp2k\ge p^2 , 为了保证算法的正确性, 我们要让这样的数落在 [L,R][L, R] 外, 也就是让 p2>Rp^2>R , 于是我们令 n=Rn=\sqrt{R} 即可.

筛法优化质因数分解

利用埃氏筛法可以快速实现素因数分解, 只要在筛的时候记录下每个数的最小素因数即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool isprime[N];
int minfac[N];

void Eratos(int n) {
memset(isprime, 1, sizeof isprime);
isprime[0] = isprime[1] = 0;
for (int i = 1; i <= n; ++i) minfac[i] = i;
for (int i = 2; i * i <= n; ++i)
if (isprime[i]) {
for (int j = i * i; j <= n; j += i) {
isprime[j] = 0;
if (minfac[j] == j) minfac[j] = i;
}
}
}

vector <int> factor(int n) {
vector <int> ret;
while (n > 1) {
ret.push_back(minfac[n]);
n /= minfac[n];
}
return ret;
}

Euler 筛法

在埃氏筛中, 30 这个数被 p=2,3,5p=2,3,5 分别筛了三次, 造成了重复, 如果我们能让每个数只被它最小的素因数筛, 就能做到 O(n)\mathcal{O}(n) 的时间复杂度.

在 Euler 筛法中, 我们从 2n2\sim n 枚举每一个数 ii , 若 ii 是素数, 则加入素数表中, 然后我们再从小到大枚举前面的素数 prime[j]prime[j] , 筛掉 iprime[j]i\cdot prime[j] , 当 prime[j]prime[j]ii 的一个素因数时, 那么后面再继续枚举到的素数一定不是所筛的数的最小素因数, 毕竟后面的素数一定大于当前的 prime[j]prime[j] , 所以这时可以终止筛选.

时间复杂度: O(n)\mathcal{O}(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int prime[N];
bool isprime[N];

void Euler(int n) {
memset(isprime, 1, sizeof isprime);
isprime[0] = isprime[1] = 0;
for (int i = 2; i <= n; ++i) {
if (isprime[i]) prime[++prime[0]] = i;
for (int j = 1; j <= prime[0] && prime[j] * i <= n; ++j) {
isprime[i * prime[j]] = 0;
if (i % prime[j] == 0) break;
}
}
}

约数

整数惟一分解定理的推论

N=p1r1p2r2pkrkN=p_1^{r_1}p_2^{r_2}\cdots p_k^{r_k}

  1. NN 的正约数的个数为 (r1+1)(r2+1)(rk+1)=i=1k(ri+1)(r_1+1)(r_2+1)\cdots(r_k+1)=\prod_{i=1}^k(r_i+1)
  2. NN 的约数个数上界为 N\sqrt{N}
  3. NN 的所有正约数的和为 (1+p1+p12++p1r1)(1+pk++pkrk)=i=1k(j=0ripij)(1+p_1+p_1^2+\cdots+p_1^{r_1})\cdots(1+p_k+\cdots+p_k^{r_k})=\prod_{i=1}^k(\sum_{j=0}^{r_i}p_i^j)
  4. 时间复杂度: O(N)\mathcal{O}(\sqrt{N})

例题

「Luoggu P1463」反素数

这道题只需要知道 121091\sim 2\cdot 10^9 内前 12 个素数的积就已经超出 NN 的范围, 所以打个表然后直接爆搜即可.

(亏我想了半天要怎么转化, 完全没考虑只有 12 个素数会被用到)

最大公约数

左转 gcd & lcm 复习笔记

高精度 gcd(a,b)\gcd{(a, b)}

更相减损术适用于大整数下求 gcd(a,b)\gcd(a, b)

  1. a<ba<b 时, gcd(a,b)=gcd(b,a)\gcd(a, b)=\gcd(b, a)
  2. a=ba=b 时, gcd(a,b)=a\gcd(a, b)=a
  3. a,ba, b 同为偶数时, gcd(a,b)=2gcd(a2,b2)\gcd(a, b)=2\gcd(\frac{a}{2}, \frac{b}{2})
  4. aa 为偶数, bb 为奇数时, gcd(a,b)=gcd(a2,b)\gcd(a, b)=\gcd(\frac{a}{2}, b)
  5. aa 为奇数, bb 为偶数时, gcd(a,b)=gcd(a,b2)\gcd(a, b)=\gcd(a, \frac{b}{2})
  6. a,ba, b 同为奇数时, gcd(a,b)=gcd(b,ab)\gcd(a, b)=\gcd(b, a-b)

容斥原理

原理很简单, 记住一个公式即可

Ai=1imAi1i<jmAiAj+1i<j<kmAiAjAk+(1)m+1A1A2Am|\cap A_i|=\sum_{1\le i\le m}|A_i|-\sum_{1\le i< j\le m}|A_i\cap A_j|+\sum_{1\le i<j<k\le m}|A_i\cap A_j\cap A_k|-\cdots+(-1)^{m+1}\sum|A_1\cap A_2\cap\cdots\cap A_m|

错排问题

  1. 利用容斥原理求解

    令集合 SS 表示 1,2,,n1,2, \cdots, n 的全排列, 则 S=n!|S|=n!

    设子集 SiS_i 表示 ii 排在第 ii 位的排列, 则 Si=(n1)!|S_i|=(n-1)!

    同理, 设 Si1Si2SikS_{i_1}\cap S_{i_2}\cap\cdots\cap S_{i_k} 表示 i1,i2,,iki_1, i_2, \cdots, i_kkk 个数排在相应位置的排列数, 则 Si1Si2Sik=(nk)!|S_{i_1}\cap S_{i_2}\cap\cdots\cap S_{i_k}|=(n-k)!

    每个元素都不排在自己位置的排列数为 Dn=S1S2Sn=SS1S2SnD_n=|\overline{S_1}\cap\overline{S_2}\cap\cdots\cap\overline{S_n}|=|S|-|S_1\cup S_2\cup\cdots\cup S_n|

    由容斥原理可得

    Dn=n!(111!+12!13!+±1n!)D_n=n!(1-\frac{1}{1!}+\frac{1}{2!}-\frac{1}{3!}+\cdots\pm\frac{1}{n!})

  2. 递推公式

    推导过程并不难想, 懒得写了, 直接上公式

    Dn=(n1)(Dn1+Dn2)D_n=(n-1)(D_{n-1}+D_{n-2})

欧拉函数

  1. gcd(a,b)=1\gcd{(a, b)}=1 , 则称 a,ba, b 互质(互素), 记作 aba\bot b
  2. 欧拉函数 φ(n)\varphi(n) 定义为 [1,n][1, n] 中与 nn 互质的数的个数.
  3. nn 为素数, 则 φ(n)=n1\varphi(n)=n-1

容斥原理求欧拉函数

根据整数惟一分解定理, 将 NN 分解为

N=p1r1p2r2pkrkN=p_1^{r_1}p_2^{r_2}\cdots p_k^{r_k}

[1,n][1, n]pip_i 的倍数的集合为 AiA_i , 则 Ai=Npi|A_i|=\lfloor \frac{N}{p_i}\rfloor

对于 pipjp_i\not=p_j , 有 AiAj=Npipj|A_i\cap A_j|=\lfloor\frac{N}{p_ip_j}\rfloor

根据容斥原理,

φ(N)=AA1A2Ak =N(Np1+Np2++Npk)+(Np1p2++Np1pk)±Np1p2pk =N(11p1)(11p2)(11pk)\begin{aligned}\varphi(N)&=|A|-|\overline{A_1}\cup\overline{A_2}\cup\cdots\cup\overline{A_k}| \\\ &=N-(\frac{N}{p_1}+\frac{N}{p_2}+\cdots+\frac{N}{p_k})+(\frac{N}{p_1p_2}+\cdots+\frac{N}{p_1p_k})-\cdots\pm\frac{N}{p_1p_2\cdots p_k} \\\ &=N(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_k})\end{aligned}

素因数分解求欧拉函数的算法

时间复杂度: O(n)\mathcal{O}(\sqrt{n})

1
2
3
4
5
6
7
8
9
10
int euler_phi(int n) {
int ret = n;
for (int i = 2; i * i <= n; ++i)
if (n % i == 0) {
ret = ret / i * (i - 1);//先除后乘可以一定程度上避免爆 int
while (n % i == 0) n /= i;
}
if (n > 1) ret = ret / n * (n - 1);
return ret;
}

埃氏筛法预处理欧拉函数值

时间复杂度: O(nloglogn)\mathcal{O}(n\log{\log{n}})

1
2
3
4
5
6
7
8
9
10
11
int phi[N];

void euler_phi(int n) {
for (int i = 2; i <= n; ++i) phi[i] = i;
for (int i = 2; i * i <= n; ++i)
if (phi[i] == i) {
phi[i] = i - 1;
for (int j = i; j <= n; j += i)//注意这里 j 的起点是 i 而不是 i * i
phi[j] = phi[j] / i * (i - 1);
}
}

欧拉函数的性质

aba\bot b , 则 φ(a)φ(b)=φ(ab)\varphi(a)\varphi(b)=\varphi(ab) , 即欧拉函数具有积性函数的性质.

上式分别将 a,ba, b 用整数惟一分解定理表示, 然后带入欧拉函数计算式即可得到证明.

例题

「POJ 3090」Visible Lattice Points

设某点坐标为 (x0,y0)(x_0, y_0) , 若该点能被看见, 则这个点一定是直线 y=y0x0xy=\frac{y_0}{x_0}x 上距离原点最近的一个整数点.

我们设一点 (x1,y1)(x_1, y_1) 满足 y1x1=y0x0,x1>x0\frac{y_1}{x_1}=\frac{y_0}{x_0}, x_1>x_0 , 则该点显然不可见, 从这个式子中我们可以知道, x1,y1x_1, y_1 必然存在一个公约数, 而 x0,y0x_0, y_0 不存在公约数, 即 x0,y0x_0, y_0 互质, 因此可见点就是图中所有满足 x0,y0x_0, y_0 互质的点再加上 (1,0),(0,1),(1,1)(1, 0), (0, 1), (1, 1) .

固定 y=y0y=y_0 , 当 x<y0x<y_0 时, 满足条件的点的数量就是 φ(y0)\varphi(y_0) , 当 x>y0x>y_0 时, 将点 (x,y0)(x, y_0)y=xy=x 对称过去得到的点就是固定 x=xix=x_iy<xiy<x_i 的情况, 根据坐标轴关于 y=xy=x 对称的性质, x<y0x<y_0x>y0x>y_0 两种情况下的答案数相等, 所以我们只需考虑 x<y0x<y_0 的情况, 然后乘 2 即可.

最终的答案为: 3+2i=2Nφ(i)3+2\cdot\sum_{i=2}^N \varphi(i)

「Luogu P2303」Longge 的问题

考虑满足 gcd(i,n)=d\gcd(i, n)=dii 的个数.

gcd(i,n)=d\gcd(i, n)=d 可以推出 gcd(id,nd)=1\gcd(\frac{i}{d}, \frac{n}{d})=1, 即 id,nd\frac{i}{d}, \frac{n}{d} 互质, 又 id<nd\frac{i}{d}<\frac{n}{d} , 所以这样的 id\frac{i}{d} 的个数为 φ(nd)\varphi(\frac{n}{d}) , 也就是 ii 的个数为 φ(nd)\varphi(\frac{n}{d})

所以答案为 dndφ(nd)\sum_{d|n}d\varphi(\frac{n}{d})

同余

剩余系

  1. 剩余系指模正整数 nn 的余数所组成的集合.
  2. 完全剩余系: 如果一个剩余系中包含了这个正整数 nn 所有可能的余数 (0,1,,n10, 1, \cdots, n-1) , 那么就称它为模 nn 的一个完全剩余系, 记作 ZnZ_n .
    简化剩余系:完全剩余系中与 nn 互质的数的集合, 记作 ZnZ_n^* .
  3. 同余等价类: ZnZ_n 里的每一个元素代表所有模 nn 意义下与它同余的整数, 我们把满足同余关系的所有整数看作一个同余等价类.
  4. ZnZ_n 中的四则运算结果全部都要在模 nn 意义下.

模运算

  1. 如果 ab(modm)a\equiv b\pmod{m} 且有 cd(modm)c\equiv d\pmod{m} , 那么:

    a+cb+d(modm)a+c\equiv b+d\pmod{m}

    acbd(modm)a-c\equiv b-d\pmod{m}

    a×cb×d(modm)a\times c\equiv b\times d\pmod{m}

  2. (a+b)modm=((amodm)+(bmodm))modm(a+b)\bmod m=((a\bmod m)+(b\bmod m))\bmod m

    (ab)modm=((amodm)(bmodm)+m)modm(a-b)\bmod m=((a\bmod m)-(b\bmod m)+m)\bmod m

    (a×b)modm=((amodm)×(bmodm))modm(a\times b)\bmod m=((a\bmod m)\times(b\bmod m))\bmod m

快速幂

右转 快速幂复习笔记

费马小定理

pp 为素数, 且 apa\bot p , 则 ap11(modp)a^{p-1}\equiv 1\pmod{p} .

因为 pp 为素数, 则 2,3,,p12, 3, \cdots, p-1pp 互质, 且不存在两个数模 pp 同余.

apa\bot p , 所以 a,2a,3a,,(p1)aa, 2a, 3a, \cdots, (p-1)app 互质, 且不存在两个数模 pp 同余.

因此 a2a3a(p1)a=(p1)!ap1a\cdot 2a\cdot 3a\cdots(p-1)a=(p-1)!a^{p-1}pp 互质, 且 (p1)!ap1(p1)!(modp)(p-1)!a^{p-1}\equiv (p-1)!\pmod{p}

两边同时除以 (p1)!(p-1)! 得到 ap11(modp)a^{p-1}\equiv 1\pmod{p}

  1. 费马小定理的另一种形式: 若 pp 为素数, 则对任意正整数 aaapa(modp)a^p\equiv a\pmod{p}.
  2. 应用: pp 是素数, a,pa, p 互质, 则 abmodp=abmod(p1)modpa^b\bmod{p}= a^{b\bmod (p-1)}\bmod{p} .

费马小定理判断素数

  1. 多次选取 aa 来判断 pp 是否满足费马小定理, 正确的概率随 aa 选取的次数的增加而增加.
  2. 时间复杂度: 设选取 kkaa , 每次用快速幂处理的时间为 O(logp)\mathcal{O}(\log{p}) , 则总时间复杂度为 O(klogp)\mathcal{O}(k\log{p}) .
  3. 由于存在 Carmichael 数, 因此上述算法存在缺陷, 即存在合数满足费马小定理.
  4. 10410^4 以内的 Carmichael 数有: 561, 1105, 1729, 2465, 2821, 6601, 8911.

欧拉定理

(在 pp 不是素数的情况下, 可以使用欧拉定理)

对于和 mm 互素的 aa , 有: aφ(m)1(modm)a^{\varphi(m)}\equiv1\pmod{m} .

证明方法和费马小定理差不多.

引理: 若 a,ma, m 互质, 满足 ax1(modm)a^x\equiv 1\pmod{m} 的最小正整数 x0x_0φ(m)\varphi(m) 的约数.

欧拉定理的推论

a,m(m>1)a, m(m>1) 互素, 则有 abmodm=abmodφ(m)modma^b\bmod{m}=a^{b\bmod\varphi(m)}\bmod m .

例题

「POJ 3696」The Luckiest number

nn 个 8 连在一起的正整数为 AnA_n , 则 An=10An1+8A_n=10A_{n-1}+8 , 容易求得 An=89(10n1)A_n=\frac{8}{9}(10^n-1) .

LAnL|A_n , 即 L89(10n1)L|\frac{8}{9}(10^n-1), 可以得到 9L8(10n1)9L|8(10^n-1) , 令 d=gcd(9L,8)d=\gcd(9L, 8) , 则有 9Ld(10n1)\frac{9L}{d}|(10^n-1)

于是 10n1(mod9Ld)10^n\equiv 1\pmod{\frac{9L}{d}}

根据欧拉定理的引理, 只需求出 φ(9Ld)\varphi(\frac{9L}{d}) , 然后枚举它的约数, 一一验证后取最小的一个数即可.

时间复杂度: O(LlogL)\mathcal{O}(\sqrt{L}\log{L})

扩展欧几里得算法

裴蜀定理 ( Bézout 定理 )

对任何整数 a,ba, b , 关于未知数 x,yx, y 的线性不定方程 ( 称为裴蜀等式 ) ax+by=cax+by=c 有整数解当且仅当 ccgcd(a,b)\gcd{(a, b)} 的倍数.

裴蜀等式有解时必有无穷多个解.

推论: a,ba, b 互素等价于 ax+by=1ax+by=1 有整数解.

例题

「BZOJ 1441」Min

假设 SS 已知, 于是有方程 A1x1+A2x2++Anxn=SA_1x_1+A_2x_2+\cdots+A_nx_n=S

根据裴蜀定理, SS 必然是 gcd(A1,A2,,An)\gcd{(A_1, A_2, \cdots, A_n)} 的倍数, 取 Smin=gcd(A1,A2,,An)S_{min}=\gcd{(A_1, A_2, \cdots, A_n)} 即可.

扩展欧几里得算法

gcd & lcm 复习笔记

根据裴蜀定理, ax+by=gcd(a,b)ax+by=\gcd{(a, b)} 有无穷组解, 而扩展欧几里得算法求出来的只是其中一组特解 (x0,y0)(x_0, y_0) .

下面考虑求 ax+by=cax+by=c 的一般解.

首先设 c=tgcd(a,b)c=t\gcd(a, b) ( tt 为整数 ) , 则使用扩展欧几里得算法求出 ax+by=gcd(a,b)ax+by=\gcd{(a, b)} 的特解 (x0,y0)(x_0, y_0) 后, 容易得到 ax+by=cax+by=c 特解为 (tx0,ty0)(tx_0, ty_0) .

由于之后考虑的是 ax+by=cax+by=c , 所以后面用 (x0,y0)(x_0, y_0) 代替 (tx0,ty0)(tx_0, ty_0) .

将方程的所有解按照 xx 的大小排序, 则特解后的下一组解 (x1,y1)(x_1, y_1) 可以表示为 (x0+d1,y0+d2)(x_0+d_1, y_0+d_2) , 其中 d1d_1 为满足条件的最小正整数.

带入方程后得到 ad1+bd2=0ad_1+bd_2=0 , 变形后有 d1d2=ba=bgcd(a,b)agcd(a,b)-\dfrac{d_1}{d_2}=\dfrac{b}{a}=\dfrac{\frac{b}{\gcd{(a, b)}}}{\frac{a}{\gcd{(a, b)}}} .

因此方程的一般解可以表示为 (x0+kbgcd(a,b),y0kagcd(a,b))(kZ)\left(x_0+k\dfrac{b}{\gcd{(a, b)}}, y_0-k\dfrac{a}{\gcd{(a, b)}}\right)(k\in \mathbf{Z}) .

为什么这里要将 ba\dfrac{b}{a} 再变形为 bgcd(a,b)agcd(a,b)\dfrac{\frac{b}{\gcd{(a, b)}}}{\frac{a}{\gcd{(a, b)}}} 呢?

因为 ba\frac{b}{a} 不是最简分数, 这一步变形是化简 ba\frac{b}{a} , 直接用 (x0+kb,y0ka)(x_0+kb, y_0-ka) 来表示特解的话会漏掉一些情况, 如 (x0+bgcd(a,b),y0agcd(a,b))(x_0+\frac{b}{\gcd{(a, b)}}, y_0-\frac{a}{\gcd{(a, b)}}) 就不能被 (x0+kb,y0ka)(x_0+kb, y_0-ka) 表示.

逆元

nn 意义下乘法的逆

如果在 ZnZ_n 中两元素 a,ba, b 满足 ab=1a\cdot b=1 , 那么我们就说 a,ba, b 互为模 nn 意义下乘法的逆元, 记作 a=b1,b=a1a=b^{-1}, b=a^{-1} .

逆元

a,ma, m 互素的时候, 若 ax1(modm)ax\equiv 1\pmod{m} , 则称 xxaa 关于模 mm 的逆元, 记作 a1a^{-1} .

[0,m)[0, m) 的范围内, 逆元是唯一的.

求解逆元

求解逆元等价于解方程 ax+my=1ax+my=1

直接使用扩展欧几里得算法即可.

欧拉定理求逆元

因为 a,ma, m 互素, 根据欧拉定理, 有 aaφ(m)11(modm)a\cdot a^{\varphi(m)-1}\equiv 1\pmod{m} , 因此 aφ(m)1a^{\varphi(m)-1} 为所求逆元.

线性求逆元: 递推法

递推法求解 1n1\sim n 的逆元.

假设当前求解的是 ii 关于模 pp 的逆元.

根据带余除法, p=iq+rp=iq+r , 两边分别对 pp 取模得到 iq+r0(modp)iq+r\equiv 0\pmod{p}

ii 不为 pp 的倍数, 则 rr 不为 0 , 因此 r1r^{-1} 存在.

在上式两边乘 i1r1i^{-1}r^{-1} 得到 qr1+i10(modp)qr^{-1}+i^{-1}\equiv 0\pmod{p} , 即

r1qr1=r1pi(modp)r^{-1}\equiv -qr^{-1}=-r^{-1}\left\lfloor \dfrac{p}{i}\right\rfloor\pmod{p}

为了防止答案为负数, 使用 r1(ppi)r^{-1}(p-\left\lfloor \frac{p}{i}\right\rfloor) 代替 r1pi-r^{-1}\left\lfloor \frac{p}{i}\right\rfloor .

时间复杂度: O(n)\mathcal{O}(n)

1
2
3
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = inv[p % i] * (p - p / i) % p;

线性求逆元: 倒推法

先直接求解 n!n! 的逆元, 然后利用 ((n1)!)1(n!)1n(modp)((n-1)!)^{-1}\equiv(n!)^{-1}\cdot n\pmod{p} 倒推求得 1!(n1)!1!\sim (n-1)! 的逆元.

接着再利用 n1(n!)1(n1)!(modp)n^{-1}\equiv (n!)^{-1}\cdot (n-1)!\pmod{p} 求解 1n1\sim n 的逆元.

例题

「POJ 1845」Sumdiv

首先利用整数惟一分解定理将 AA 表示为 A=p1r1p2r2pnrnA=p_1^{r_1}p_2^{r_2}\cdots p_n^{r_n} .

ABA^B 约数和为

(1+p1+p12++p1Br1)(1+p2+p22++p2Br2)(1+pn+pn2++pnBrn)(1+p_1+p_1^2+\cdots +p_1^{B\cdot r_1})(1+p_2+p_2^2+\cdots+p_2^{B\cdot r_2})\cdots(1+p_n+p_n^2+\cdots+p_n^{B\cdot r_n})

可以发现, 每一个括号里都是一个等比数列求和, 以 pip_i 为例, 它的和可以写成 piBri+11pi1\dfrac{p_i^{B\cdot r_i+1}-1}{p_i-1} , 然后我们可以利用快速幂求解分子 piBri+1mod9901p_i^{B\cdot r_i+1}\bmod{9901} , 这时若 pi1>(piBri+11)mod9901p_i-1>(p_i^{B\cdot r_i+1}-1)\bmod{9901} , 我们会得到 00 , 这显然是错的, 而模数 99019901 正好为素数, 因此考虑用 pi1p_i-1 的逆元与分子的结果相乘来代替除法.

pi1p_i-199019901 的倍数时不存在逆元, 这时我们可以发现 pi1(mod9901)p_i\equiv 1\pmod{9901} , 因此 1,pi,pi2,,piBri+11(mod9901)1, p_i, p_i^2, \cdots, p_i^{B\cdot r_i+1}\equiv 1\pmod{9901} , 所以 1+pi+pi2++piBriBri+1(mod9901)1+p_i+p_i^2+\cdots+p_i^{B\cdot r_i}\equiv B\cdot r_i+1\pmod{9901} .

线性同余方程

形如 axc(modm)ax\equiv c\pmod{m} 的方程,称为线性同余方程。

  1. 朴素算法
    x=1,2,,m1x=1, 2, \cdots, m-1 一次带进去验证即可,这个朴素算法的复杂度取决于 mm
  2. 扩展欧几里得算法
    可以将方程看作 ax+my=cax+my=c ,然后利用扩展欧几里得方法求解。
    根据裴蜀定理,当且仅当 gcd(a,m)c\gcd{(a, m)}|c 时方程有解。
    d=gcd(a,m)d=\gcd(a, m) ,则方程一共有 dd 个解,我们用扩展欧几里得算法求得特解 x0x_0 ,则方程的通解 xi=x0+imd(i=0,1,,d1)x_i=x_0+i\cdot \dfrac{m}{d}(i=0, 1, \cdots, d-1)
  3. 欧拉定理
    d=gcd(a,m)d=\gcd{(a, m)} ,若 dcd \nmid c 则方程无解。
    然后令 a=ad,c=cd,m=mda'=\dfrac{a}{d}, c'=\dfrac{c}{d}, m'=\dfrac{m}{d} ,则 gcd(a,m)=1\gcd{(a', m')}=1 ,原方程变为 axc(modm)a'x\equiv c'\pmod{m'} ,由欧拉定理,aφ(m)1(modm)a'^{\varphi(m')}\equiv 1\pmod{m'} ,在方程两边分别乘上上式,得到 axcaφ(m)(modm)a'x\equiv c'a'^{\varphi(m')}\pmod{m'} ,即 xcaφ(m)1(modm)x\equiv c'a'^{\varphi(m')-1}\pmod{m'} ,于是我们得到原方程的特解 x0=dcaφ(m)1x_0=dc'a'^{\varphi(m')-1} ,因此原方程的解为 xi=x0+im(i=1,2,,d1)x_i=x_0+i\cdot m'(i=1, 2, \cdots,d-1)

例题

线性组合

对应整数数列 A1,A2,,AnA_1, A_2, \cdots, A_n 是否存在 X1,X2,,XnX_1, X_2, \cdots, X_n , 使得 A1X1+A2X2++AnXn=CA_1X_1+A_2X_2+\cdots+A_nX_n=C ,其中 gcd(A1,A2,,An)C(n2)\gcd{(A_1, A_2, \cdots, A_n)}\mid C(n\ge2) 。如有,请找出一组整数解 (x1,x2,x3,x4)(x_1, x_2, x_3, x_4) 满足 12x1+24x2+18x3+15x4=312x_1+24x_2+18x_3+15x_4=3 .

题中的方程是 n 元形式的扩展欧几里得方程,我们尝试将 n 元逐步降为二元得到最终解。

d=gcd(A1,A2,,An)d=\gcd{(A_1, A_2, \cdots, A_n)} ,令 C=Cd,Ai=Aid(i=1,2,,n)C'=\dfrac{C}{d},A_i'=\dfrac{A_i}{d}(i=1, 2, \cdots, n) ,首先将方程变形为 A1X1+A2X2++AnXn=CA_1'X_1+A_2'X_2+\cdots+A_n'X_n=C' ,此时满足 gcd(A1,A2,,An,C)=1\gcd{(A_1', A_2', \cdots, A_n', C')}=1

然后我们考虑求解方程 A1X1+gcd(A2,A3,,An)Y1=CA_1'X_1+\gcd(A_2', A_3', \cdots, A_n')Y_1=C' ,利用扩展欧几里得方法求得特解 X1,Y1X_1,Y_1 ,由于题中仅要求得到一组解,因此我们只取特解即可,然后再以同样的方法求解 A2X2+A3X3++AnXn=gcd(A2,A3,,An)Y1A_2'X_2+A_3'X_3+\cdots+A_n'X_n=\gcd(A_2', A_3', \cdots, A_n')Y_1 便可以实现逐渐降元了。

线性同余方程组(模互素)

考虑形如 xai(modmi)x\equiv a_i\pmod{m_i} 的若干个线性同余方程联合得到的方程组,例如

{x2(mod3)x3(mod5)x5(mod7)\begin{cases} x &\equiv& 2\pmod{3} \\ x &\equiv& 3\pmod{5} \\ x &\equiv& 5\pmod{7} \end{cases}

一种可行的解法是令 x=3y+2x=3y+2 ,然后将方程 (2)(2) 改写成 y2(mod5)y\equiv 2\pmod{5} ,接着再令 y=5z+2y=5z+2 代入方程 (3)(3) 得到 z4(mod7)z\equiv 4\pmod{7} ,再令 z=7k+4z=7k+4 ,最后我们可以得到解 x68(mod105)x\equiv68\pmod{105}

中国剩余定理

若线性同余方程组 xai(modmi)(i=1,,n)x\equiv a_i\pmod{m_i}(i=1, \cdots, n) 的模数 mim_i 两两互素,则 xxmodM(M=i=1nmi)\bmod{M}(M=\prod_{i=1}^n m_i) 下有唯一解。

Mi=MmiM_i=\dfrac{M}{m_i} ,有 gcd(Mi,mi)=1\gcd{(M_i, m_i)}=1 ,因此 MiM_imodmi\bmod{m_i} 下的逆元存在,设其为 tit_i ,于是有 aiMitiai(modmi),aiMiti0(modmj)(ji)a_iM_it_i\equiv a_i\pmod{m_i},a_iM_it_i\equiv0\pmod{m_j}(j\not= i)

可以得到解 xi=1naiMiti(modM)x\equiv\sum_{i=1}^na_iM_it_i\pmod{M}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//China Remainer Theorem
int CRT(const int a[], const int m[], int n) {
int M = 1, ret = 0;
for (int i = 1; i <= n; ++i) M *= m[i];
for (int i = 1; i <= n; ++i) {
int ti = getinv(m[i], a[i]), Mi = M / m[i];
ret = (ret + a[i] * ti * Mi % M) % M;
}
return ret;
}

//利用 exgcd 求逆元
int getinv(int a, int b) {
int x, y;
exgcd(a, b, x, y);
return x;
}

线性同余方程组(模不互素)

对于模不互素的线性同余方程组

{xa1(modm1)xa2(modm2)xan(modmn)\begin{cases} x &\equiv a_1\pmod{m_1} \\ x &\equiv a_2\pmod{m_2} \\ &\cdots \\ x &\equiv a_n\pmod{m_n} \end{cases}

考虑求解方程数量为 2 的情形,当方程数量大于 2 时迭代求解。

{xa1(modm1)xa2(modm2)\begin{cases} x &\equiv a_1\pmod{m_1} \\ x &\equiv a_2\pmod{m_2} \end{cases}

y=xa1y=x-a_1 ,则方程组变为

{y0(modm1)ya2a1(modm2)\begin{cases} y &\equiv 0 &\pmod{m_1} \\ y &\equiv a_2-a_1 &\pmod{m_2} \end{cases}

d=gcd(m1,m2)d=\gcd{(m_1, m_2)} ,根据裴蜀定理,若 d(a2a1)d \nmid (a_2-a_1) ,则方程组无解,否则令 y=yd,a=a2a1d,mi=m1d(i=1,2)y'=\dfrac{y}{d},a'=\dfrac{a_2-a_1}{d}, m_i'=\dfrac{m_1}{d}(i=1,2) ,有

{y0(modm1)ya(modm2)\begin{cases} y' &\equiv{0} &\pmod{m_1'} \\ y' &\equiv{a'} &\pmod{m_2'} \end{cases}

此时 gcd(m1,m2)=1\gcd{(m_1', m_2')}=1 ,令 y=km1y'=km_1' ,再由欧拉定理可得 kam1φ(m2)1(modm2)k\equiv{a'm_1'^{\varphi(m_2')-1}}\pmod{m_2'} ,于是 ydam1φ(m2)(moddm1m2)y\equiv{da'm_1'^{\varphi{(m_2')}}}\pmod{dm_1'm_2'} ,最后代回去可以得到 x(a2a1)m1φ(m2)+a1(moddm1m2)x\equiv{(a_2-a_1)m_1'^{\varphi{(m_2')}}+a_1}\pmod{dm_1'm_2'}

例题

「POJ 2891」Strange Way to Express Integers

算法和上述一致。

假设我们已经通过迭代求解得到前 k1k-1 个方程的一个解 x0x_0 ,令 M=i=1k1miM=\prod_{i=1}^{k-1}m_i ,则 x=x0+iMx=x_0+i\cdot M 是前 k1k-1 个方程的通解。

考虑第 kk 个方程,我们可以将其改写为 x0+tMak(modmk)x_0+t\cdot M\equiv{a_k}\pmod{m_k} ,即 tMakx0(modmk)t\cdot M\equiv{a_k-x_0}\pmod{m_k} ,接下来找到一个合适的 tt 即可,也就是求解一个同余方程了,利用欧拉定理求解即可。

高次同余方程

第一类高次同余方程

形如 axb(modp)a^x\equiv{b}\pmod{p} 的方程称为第一类高次同余方程,其中 gcd(a,p)=1\gcd(a, p)=1

BSGS

解决这一类方程主要采用 BSGS (Baby Step Giant Step) 算法。

xx 写成 x=ktm(0mt1)x=kt-m(0\le m\le t-1) 的形式,则原方程可变形为 (at)kbam(modp)(a^t)^k\equiv{b\cdot a^m\pmod{p}}

假设 tt 已知,那么我们只需枚举 kk ,然后计算相应的 bamb\cdot a^m ,判断是否满足变形后的等式即可。

因为在枚举 kk 的过程中 mm 的取值也在变化,但 mm 的取值范围是不变的,因此我们可以预处理所有可能的 bamb\cdot a^m 值,用一个 Hash 表储存。

由欧拉定理可知,有 aφ(p)1(modp)a^{\varphi(p)}\equiv{1}\pmod{p} ,而 φ(p)<p\varphi(p)<p ,因此从 1~p ,模 pp 意义下 aa 的幂至少完成了一次循环,所以最小整数解 x<px<p ,进而我们可以从 00pt\dfrac{p}{t} 枚举 kk ,而 m[0,t1]m\in[0,t-1] ,因此时间复杂度为 O(pt+t)\mathcal{O}(\dfrac{p}{t}+t) ,当 t=pt=\sqrt{p} 时复杂度最小。

时间复杂度:O(2p)\mathcal{O}(2\sqrt{p})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int BSGS(int a, int b, int p) {
b %= p;
if (a % p == 0) return b ? 1 : -1;
map <int, int> hash;
int t = sqrt(p) + 1;
for (int i = 0; i < t; ++i) {
int val = (long long)b * power(a, i, p) % p;//快速幂
hash[val] = i;
}
a = power(a, t, p);
for (int i = 0; i <= t; ++i) {
int val = power(a, i, p);
int j = hash.find(val) == hash.end() ? -1 : hash[val];
if (j >= 0 && i * t - j >= 0) return i * t - j;
}
return -1;
}

第二类高次同余方程

形如 xkb(modp)x^k\equiv{b}\pmod{p} 的方程称为第二类高次同余方程。

首先引入原根的概念,这一部分和抽象代数相关,我目前还不会 QAQ 。

满足同余式 an1(modp)a^n\equiv{1}\pmod{p} 的最小整数 nn 称为 aapp 的阶,记作 δp(a)\delta_p(a)

由欧拉定理可知,当 gcd(a,p)=1\gcd(a, p)=1 时,有 aφ(p)1(modp)a^{\varphi(p)}\equiv{1}\pmod{p} ,因此阶的存在性得证。

性质 1a,a2,,aδp(a)a,a^2,\cdots,a^{\delta_p(a)}mm 两两不同余。

性质 2 :若 an1(modp)a^n\equiv{1}\pmod{p} ,则 nδp(a)n \mid \delta_p(a)

推论 :若 aman(modp)a^m\equiv{a^n}\pmod{p} ,则 mn(modδp(a))m\equiv{n}\pmod{\delta_p(a)}

性质 3 :设 pN,a,bZ,gcd(a,p)=gcd(b,p)=1p\in\mathbb{N}^*,\, a,b\in\mathbb{Z},\gcd(a,p)=\gcd(b,p)=1 ,则 δp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b) 的充要条件是 gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1

性质 4 :设 kN,pN,aZ,gcd(a,p)=1k\in\mathbb{N},p\in\mathbb{N}^*,a\in\mathbb{Z},\gcd(a,p)=1 ,则 δp(ak)=δp(a)gcd(δp(a),k)\delta_p(a^k)=\dfrac{\delta_p(a)}{\gcd(\delta_p(a),k)}

原根

pN,aZp\in\mathbb{N}^*,a\in\mathbb{Z} ,若 gcd(a,p)=1\gcd(a,p)=1 ,且 δp(a)=φ(p)\delta_p(a)=\varphi(p) ,则称 aa 为模 mm 的原根。

以下基于 pp 为质数的情况进行讨论。

如何判断 aa 是否是 pp 的原根?

由费马小定理,方程 ax1(modp)a^x\equiv{1}\pmod{p} 的一个解为 x=p1x=p-1 ,因此最小整数解 xmin(p1)x_{min}\mid (p-1) ,而满足 xmin=p1x_{min}=p-1 的整数 aapp 的原根,所以我们逐个枚举 p1p-1 的约数 kk ,如果只有当 k=p1k=p-1 时满足 ak1(modp)a^k\equiv{1}\pmod{p} ,则 aapp 的原根。

下面考虑求解 xkb(modp)x^k\equiv{b}\pmod{p}

阶的性质 1 ,存在唯一的正整数 mm ,满足 amb(modp),0mp1a^m\equiv{b}\pmod{p},0\le m\le p-1 ,其中 aapp 的原根,mm 可以由第一类高次同余方程求出。

同样,存在唯一正整数 yy ,使得 xay(modp)x\equiv{a^y}\pmod{p} ,于是上述方程变为 akyam(modp)a^{ky}\equiv{a^m}\pmod{p}

阶的性质 2推论kym(modp1)ky\equiv{m}\pmod{p-1} ,于是我们求解这个线性同余方程得到 yy 便可以得到 xx

Author: f1a3h

Permalink: https://blog.rbkou.me/Algorithm/basic-number-theory/

文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。

Comments