基础数论

1 同余

1.1 定义

\(a,b\) 为两个整数,且 \(a-b\) 能被自然数 \(m\) 整除,就说 \(a\)\(b\) 关于模 \(m\) 同余,记作 \(a\equiv b\pmod{m}\)

1.2 性质

同余有以下性质:

  1. 自反性: \(a\equiv a\pmod{m}\)
  2. 对称性:若 \(a\equiv b\pmod{m}\),则 \(b\equiv a\pmod{m}\)
  3. 传递性:若 \(a\equiv b\pmod{m}\)\(b\equiv c\pmod{m}\),则 \(a\equiv c\pmod{m}\)
  4. 同加性:若 \(a\equiv b\pmod{m}\),则 \(a+c\equiv b+c\pmod{m}\)
  5. 同乘性:若 \(a\equiv b\pmod{m}\),则 \(ac\equiv bc\pmod{m}\)
  6. 同幂性:若 \(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. 如果当前数没有被筛掉,则必定是素数,记录。

  3. 枚举已记录的素数

    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\) 互质的数的个数。

  • 通项公式为:

\[\varphi(n)=n\times \prod\limits_{i=1}^k(1-\frac{1}{p_i}) \]

​ 其中,\(p_i\)\(n\) 的所有质因数。

2.5.2 性质

  1. \(\varphi(1)=1\)
  2. \(p\) 为素数,则 \(\varphi(p)=p-1\)
  3. \(p\) 为素数,则 \(\varphi(p^k)=(p-1)\times p^{k-1}\)
  4. 欧拉函数是积性函数

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\),则:

\[a^{\varphi(m)}\equiv1\pmod{m} \]

特别的,当 \(m\) 为质数时,根据欧拉函数的定义代入,即可得到费马小定理。

2.7.2 费马小定理

\(m\) 为素数,则:

\[a^{m-1}\equiv1\pmod{m} \]

2.7.3 扩展欧拉定理

\[a^b\equiv\begin{cases}a^{b\bmod{\varphi(p)}},&\gcd(a,p)=1\\a^b,&\gcd(a,p)\ne1,b<\varphi(p)\pmod{p}\\a^{b\bmod{\varphi(p)}+\varphi(p)},&\gcd(a,p)\ne1,b\ge\varphi(p)\end{cases} \]

对于 \(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 扩欧后构造通解

通解公式为:

\[\begin{cases}x=x_0+\frac{b}{\gcd(a,b)}k\\y=y_0-\frac{a}{\gcd(a,b)}k\end{cases} \]

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\)

posted @ 2024-02-27 17:53  UKE_Automation  阅读(10)  评论(0编辑  收藏  举报