基础数论
1 同余
1.1 定义
若 \(a,b\) 为两个整数,且 \(a-b\) 能被自然数 \(m\) 整除,就说 \(a\) 和 \(b\) 关于模 \(m\) 同余,记作 \(a\equiv b\pmod{m}\)。
1.2 性质
同余有以下性质:
- 自反性: \(a\equiv a\pmod{m}\)。
- 对称性:若 \(a\equiv b\pmod{m}\),则 \(b\equiv a\pmod{m}\)。
- 传递性:若 \(a\equiv b\pmod{m}\),\(b\equiv c\pmod{m}\),则 \(a\equiv c\pmod{m}\)。
- 同加性:若 \(a\equiv b\pmod{m}\),则 \(a+c\equiv b+c\pmod{m}\)。
- 同乘性:若 \(a\equiv b\pmod{m}\),则 \(ac\equiv bc\pmod{m}\)
- 同幂性:若 \(a\equiv b\pmod{m}\),则 \(a^c\equiv b^c\pmod{m}\)
注意同余没有同除性,但是有消去律。
2 素数
2.1 素数的定义
一个大于 \(1\) 的自然数,除了 \(1\) 和他自身外,不能被其他自然数整除的数叫做素数。
2.2 有关素数的定理
2.2.1 算数基本定理
任何一个大于 \(1\) 得正整数都能被唯一分解为有限个素数的乘积。即:
\(N=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\)
其中,\(c_i\) 均为正整数,\(p_i\) 均为素数。
2.2.2 \(N\) 与其因子的大小关系
\(N\) 中至多只有一个大于 \(\sqrt{N}\) 的因子。
2.2.3 分解质因数
采用试除法,从 \(2\) 到 \(\sqrt{n}\) 枚举。最坏时间复杂度 \(O(\sqrt{n})\)
2.3 素数判定
仍然考虑试除法,复杂度 \(O(\sqrt{n})\)。
2.4 筛素数
2.4.1 埃氏筛
对于求出某个范围内的所有素数,我们从 \(2\) 开始,将 \(2\) 的倍数去掉;然后是 \(3\), 将 \(3\) 的倍数去掉;……以此类推。
这样做的时间复杂度为 \(O(n\log\log n)\),原因在于一个合数会被多次筛去,浪费了时间。
2.4.2 线性筛(欧拉筛)
为了解决埃氏筛的弊端,每个数现在只会被它的最小质因子筛掉。
流程如下:
从小到大枚举每个数
如果当前数没有被筛掉,则必定是素数,记录。
枚举已记录的素数
1). 如果合数没有越界,划掉合数
2). 如果 \(i\bmod{p}=0\),表明 \(i\) 有了一个最小质因子 \(p\)。根据欧拉筛的定义,到此退出即可。
代码:
int prim[Maxn], tot;
bool vis[Maxn];
void prime() {
for(int i = 2; i <= Maxn; i++) {
if(!vis[i]) prim[++tot] = i;
for(int j = 1; i * prim[j] <= Maxn; j++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0) break;
}
}
}
2.5 欧拉函数
2.5.1 定义
-
对于正整数 \(n\),他的欧拉函数 \(\varphi(n)\) 指的是小于等于 \(n\) 的数中与 \(n\) 互质的数的个数。
-
通项公式为:
其中,\(p_i\) 为 \(n\) 的所有质因数。
2.5.2 性质
- \(\varphi(1)=1\)
- 若 \(p\) 为素数,则 \(\varphi(p)=p-1\)
- 若 \(p\) 为素数,则 \(\varphi(p^k)=(p-1)\times p^{k-1}\)
- 欧拉函数是积性函数。
2.5.3 求解欧拉函数
2.5.3.1 试除法
如果只要求一个数的欧拉函数,直接根据定义试除法即可。
int get_phi(int n) {
int ans = n;
int m = sqrt(n);
for(int i = 2; i <= m; i++) {
if(n % i == 0) {
ans = ans * (i - 1) / i;
}
while(n % i == 0) n /= i;
}
if(n > 1) ans = ans * (n - 1) / n;
return ans;
}
2.5.3.2 筛法求欧拉函数
上面算法的时间复杂度为 \(O(\sqrt{n})\),如果要求 \(1\) 到 \(n\) 所有数的欧拉函数,总复杂度是 \(O(n\sqrt{n})\),不够优。
我们考虑在筛法的过程中求出欧拉函数。
设 \(phi_i\) 表示 \(\varphi(i)\)。
若当前 \(i\) 为素数,则 \(phi_i=i-1\)。
若 \(i\) 不为素数:
设 \(m\) 的最小质因子为 \(p_j\),则 \(m\) 应该被 \(i \times p_j\) 筛去。
1). 若 \(i\) 能被 \(p_j\) 整除,则 \(i\) 包含了 \(m\) 所有的质因子。
则 \(phi_m=m\times\prod\limits_{i=1}^k\frac{p_i-1}{p_i}=p_j\times i\times\prod\limits_{i=1}^k\frac{p_i-1}{p_i}=p_j\times phi_i\)。
2). 若 \(i\) 不被 \(p_j\) 整除,则 \(i\) 与 \(p_j\) 互质。
则 \(phi_m=phi_{p_j\times i}=phi_{p_j}\times phi_i=(p_j-1)\times phi_i\)。
代码:
bool vis[Maxn];
int prim[Maxn], phi[Maxn], cnt;
void get_phi() {
phi[1] = 1;
for(int i = 2; i <= Maxn; i++) {
if(!vis[i]) {
prim[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; i * prim[j] <= Maxn; j++) {
vis[i * prim[j]] = 1;
phi[i * prim[j]] = (prim[j] - 1) * phi[i];
if(i % prim[j] == 0) {
phi[i * prim[j]] = prim[j] * phi[i];
break;
}
}
}
}
2.6 求约数个数和约数和
2.6.1 约数个数
2.6.1.1 公式法
根据算数基本定理,设:
\(n=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\)
则 \(n\) 的约数个数 \(d(n)=(c_1+1)(c_2+1)\cdots(c_m+1)\)。
由乘法原理易证。
2.6.1.2 筛法求约数个数
设 \(d_i\) 为 \(i\) 的约数个数,\(num_i\) 为 \(i\) 的最小质因数个数。
若 \(i\) 为素数,则 \(d_i=2,num_i=1\)。
若 \(i\) 为合数:
设 \(m\) 的最小质因子为 \(p_j\),则 \(m\) 应该被 \(i \times p_j\) 筛去。
1). 当 \(i\) 被 \(p_j\) 整除时
设 \(i=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\),
则 \(d_m=d_{i\times p_j}=(1 + c_1+1)(1+c_2)\cdots(1+c_m)\)。
即 \(d_m=d_i\div (num_i+1)\times(num_i+2)\)
同时,\(num_m=num_i+1\)。
2). 当 \(i\) 不被 \(p_j\) 整除时
设 \(i=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\),
则 \(d_m=d_{p_j\times i}=(1+c_1)(1+c_2)\cdots(1+c_m)(1+1)=2\cdot d_i\)。
同时 \(num_m=1\)。
代码跟筛法求欧拉函数差不多,全部带入公式计算即可。
2.6.2 约数和
2.6.2.1 公式法
根据算数基本定理,设:
\(n=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\)
则 \(n\) 的约数个数为 \((p_1^0+p_1^1+p_1^2+\cdots p_1^{r_1})(p_2^0+p_2^1+p_2^2+\cdots p_2^{r_2})\cdots(p_m^0+p_m^1+p_m^2+\cdots p_m^{r_m})\)。
2.6.2.2 筛法求约数之和
设 \(d_i\) 表示 \(i\) 的约数之和,\(num_i\) 表示是最小值因子所在的项,即 \((p_1^0+p_1^1+p_1^2+\cdots p_1^{r_1})\)。
若 \(i\) 为素数,则 \(d_i=num_i=i+1\)。
若 \(i\) 为合数:
设 \(m\) 的最小质因子为 \(p_j\),则 \(m\) 应该被 \(i \times p_j\) 筛去。
1). 当 \(i\) 被 \(p_j\) 整除时
设 \(i=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\),
则 \(d_m=d_{i\times p_j}=(p_1^0+p_1^1+p_1^2+\cdots p_1^{r_1+1})(p_2^0+p_2^1+p_2^2+\cdots p_2^{r_2})\cdots(p_m^0+p_m^1+p_m^2+\cdots p_m^{r_m})\)。
即 \(d_m=d_i\div num_i\times(num_i+p_j^{r_j+1})=d_i\div num_i\times (num_i\times p_j+1)\)
同时,\(num_m=num_i\times p_j+1\)。
2). 当 \(i\) 不被 \(p_j\) 整除时
设 \(i=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\),
则 \(d_m=d_{p_j\times i}=(p_1^0+p_1^1+p_1^2+\cdots p_1^{r_1+1})(p_2^0+p_2^1+p_2^2+\cdots p_2^{r_2})\cdots(p_m^0+p_m^1+p_m^2+\cdots p_m^{r_m})(p_j^0+p_j^1)=d_i(1+p_j)\)。
同时 \(num_m=p_j+1\)。
代码与筛法求约数个数一样,就不放了。
2.7 欧拉定理与费马小定理
2.7.1 欧拉定理
若 \(\gcd(a,m)=1\),则:
特别的,当 \(m\) 为质数时,根据欧拉函数的定义代入,即可得到费马小定理。
2.7.2 费马小定理
若 \(m\) 为素数,则:
2.7.3 扩展欧拉定理
对于 \(b\) 较大的情况,可以用扩展欧拉定理将 \(b\) 降下去,然后进行快速幂。
2.7.3.1 秦九昭算法
对于多项式 \(f(x)=a_nx^n+a_{n-1}x^{n-1}+a_{n-2}x^{n-2}\cdots+a_{1}x+a_0\),一般人工计算需要进行 \(\frac{n(n+1)}{2}\) 次乘法,\(n\) 次加法。
对于 \(f(x)\) 进行变形:
\(\begin{aligned}f(x) & = a_nx^n+a_{n-1}x^{n-1}+a_{n-2}x^{n-2}\cdots+a_{1}x+a_0 \\ & = (a_nx^{n-1}+a_{n-1}x^{n-2}+a_{n-2}x^{n-3}\cdots+a_{1})x+a_0\\&=\cdots\\&=(\dots((a_nx+a_{n-1})x+a_{n-2})x\dots+a_1)x\end{aligned}\)
这样就只需要做 \(n\) 次乘法,\(n\) 次加法即可
扩展欧拉定理模板:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
int a, p, b;
string s;
int get_phi(int k) {
int ans = k, m = sqrt(k);
for(int i = 2; i <= m; i++) {
if(k % i == 0) {
ans = ans * (i - 1) / i;
}
while(k % i == 0) k /= i;
}
if(k > 1) ans = ans * (k - 1) / k;
return ans;
}
int down_pow(int phi) {
int res = 0;
bool flag;
for(int i = 0; i < s.size(); i++) {
res = res * 10 + s[i] - 48;
if(res >= phi) {
res %= phi;
flag = 1;
}
}
if(flag) res += phi;
return res;
}
int qpow(int a, int b, int p) {
int res = 1;
while(b) {
if(b & 1) {
res *= a;
res %= p;
}
a *= a;
a %= p;
b >>= 1;
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin >> a >> p >> s;
int _phi = get_phi(p);
b = down_pow(_phi);
cout << qpow(a, b, p);
return 0;
}
其中函数 down_pow
部分就利用到了秦九昭算法来对 \(b\) 降幂。
3 最大公约数
3.1 欧几里得算法
欧几里得算法是常用的求 \(\gcd\) 的算法之一。
他运用了一个性质:\(\gcd(a,b)=\gcd(b,a\bmod{b})\)。
代码:
void gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
3.2 裴蜀定理
定理说明,设 \(a,b\) 是不全为 \(0\) 的整数,则存在 \(x,y\),使得
\(ax+by=\gcd(a,b)\)。
3.2.1 裴蜀定理的简单推广
设 \(a_1,a_2,\cdots a_n\) 是不全为 \(0\) 的整数,则存在 \(x_1,x_2,\cdots,x_n\),使得:
\(\sum\limits_{i=1}^na_ix_i=\gcd(a_1,a_2,\cdots,a_n)\)。
3.3 扩展欧几里得算法(扩欧)
扩欧算法常用于求方程 \(ax+by=\gcd(a,b)\) 的整数解(特解)。
推导:
\(ax_1+by_1=gcd(a,b)\\\)
\(bx_2+(b\%a)y_2=gcd(b,a\%b)\)
\(\because gcd(a,b)=gcd(b,a\%b)\)
\(\therefore ax_1+by_1=bx_2+(b\%a)y_2\)
\(\because a\%b=a-\left\lfloor\dfrac{a}{b}\right\rfloor\times b\)
\(\therefore ax_1+by_1=bx_2+(a-\left\lfloor\dfrac{a}{b}\right\rfloor\times b)y_2=ay_2+b(x_2-\left\lfloor\dfrac{a}{b}\right\rfloor y_2)\)
因此,我们可以层层向下递归求解,然后回带到上一层,
代码:
int exgcd(int a, int b, int &x, int &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
int ret = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return ret;
}
3.3.1 扩欧后构造通解
通解公式为:
3.3.2 扩欧求解线性同余方程
问题:求方程 \(ax\equiv b\pmod{p}\) 的一个整数解。
将同余方程转化为不定方程:
由 \(ax\equiv b\pmod{p}\) 得 \(ax=p(-y)+b\),即 \(ax+py=b\)。
由裴蜀定理知,当 \(b\mid \gcd(a,p)\) 时有解。
接下来由扩欧求出方程 \(ax+py=\gcd(a,p)\) 的特解,最后把得到的 \(x\) 乘上 \(\frac{b}{\gcd(a,p)}\) 就是原方程特解。
3.3.2.1 同余方程的消去律
虽然同余没有同除性,但同余方程有消去律。
若 \(ax\equiv bx\pmod{p}\),则 \(a\equiv b\pmod{\dfrac{p}{\gcd(x, p)}}\)。
4 逆元
4.1 定义与用途
若 \(ax\equiv 1\pmod{p}\),称 \(x\) 为 \(a\) 在模 \(p\) 意义下的乘法逆元,记作 \(a^{-1}\)。
仅当 \(\gcd(a,p)=1\) 时存在乘法逆元。
上面曾经讲到,同余不具有同除性,这是不能忍受的。于是我们就可以用逆元来解决问题。
根据定义推到,得出的结论是:\(\frac{a}{b}\bmod{p}=a\times(b^{-1})\bmod{p}\),于是就转化为了乘法问题。
4.2 求乘法逆元
4.2.1 费马小定理
条件:当 \(p\) 为素数时,可以用费马小定理求解。
根据费马小定理 \(a^{p-1}\equiv1\pmod{p}\),可得 \(a\times a^{p-2}\equiv\pmod{p}\)。
所以此时的乘法逆元为 \(a^{p-2}\),用快速幂求解即可。
4.2.2 线性求逆元
求 \(1\) 到 \(p-1\) 中每个数关于模 \(p\) 的逆元(\(p\) 为素数)。
推导:
首先,\(1^{-1}\equiv1\pmod{p}\)
接下来,设 \(p=ki+r\),其中 \(1<i<p,r<i\)。
在模 \(p\) 意义下,得到 \(ki+r\equiv0\pmod{p}\)。
两边同乘 \(i^{-1}\times r^{-1}\),则:
\(k\times i\times i^{-1}\times r^{-1}+r\times i^{-1}\times r^{-1}\equiv 0\pmod{p}\)
\(k\times r^{-1}+i^{-1}\equiv 0\pmod{p}\)
\(i^{-1}\equiv-k\times r^{-1}\pmod{p}\)
\(i^{-1}\equiv-\left\lfloor\dfrac{p}{i}\right\rfloor\times r^{-1}\pmod{p}\)
\(i^{-1}\equiv-\left\lfloor\dfrac{p}{i}\right\rfloor\times(p\bmod{i})^{-1}\pmod{p}\)
设 \(i^{-1}\) 为 \(inv_i\),则:
\(inv_i=(\frac{p}{i}\times inv_{p\bmod{i}})\bmod{p}\)。
设 \(i^{-1}\) 为 \(inv_i\),则:
\(inv_i=(\frac{p}{i}\times inv_{p\bmod{i}})\bmod{p}\)。
若要求正整数,则递推公式为 :
\(inv_i=((p-\frac{p}{i})\times inv_{p\bmod{i}})\bmod{p}\)。
代码:
inv[1] = 1;
for(int i = 2; i <= n; i++) {
inv[i] = ((p - p / i) * inv[p % i]) % p;
}
4.2.3 欧拉定理求逆元
回顾欧拉定理:当 \(\gcd(a,n)=1\) 时,\(a^{\varphi(p)}\equiv1\pmod{p}\)。所以此时 \(a^{\varphi(p)-1}\) 就是 \(a\) 关于 \(n\) 的逆元。
4.2.4 扩欧求逆元
看 \(ax\equiv1\pmod{b}\),理应有一种熟悉的感觉。他就是一个同余方程,所以可以扩欧。要求 \(\gcd(a,b)=1\)。