TonyYin's Blog

Back

带余除法#

整除#

a,ba,b 是整数,a0a\not= 0,如果存在 qq 是整数,使得 b=aqb=aq,那么有 bb 可被 aa 整除,记作 aba \mid b,且称 bbaa倍数aabb因数

性质#

  1. aba \mid bbcb \mid c,则 x,y\forall x,y,有axb+yca \mid xb+yc.
  2. aba \mid bbcb \mid c,则 aca \mid c.
  3. m0m\neq 0,则 aba\mid b,当且仅当 mambma\mid mb.
  4. aba\mid bbab\mid a,则 a=±ba=\pm b.
  5. aba \mid bb0b\not= 0,则 ab |a|\leq |b|.

带余除法#

a,ba,b 是正整数,且 b0b\not= 0,则存在唯一整数 q,rq, r 使得 a=qb+r,0r<ba = qb+r,0\leq r < |b|.

这个式子叫做带余除法,记余数 r=amodbr=a\bmod b。当 r=0r=0 时,bbaa 的因数。

性质#

(a±b)modp=(amodp±bmodp)modp(a\pm b)\bmod p=(a\bmod p\pm b\bmod p)\bmod p (a×b)modp=(amodp×bmodp)modp(a\times b)\bmod p=(a\bmod p\times b\bmod p)\bmod p

最大公因数与最小公倍数#

最大公因数#

aabb 是两个非零整数,如果 dad \mid adbd \mid b,则称 ddaabb公因数

所有公因数中最大的被称为 a,ba,b最大公因数,记为 gcd(a,b)\gcd(a,b).

特殊地,当 gcd(a,b)=1\gcd(a,b)=1时,称 a,ba,b 互质

最小公倍数#

aabb 是两个非零整数,如果 ada \mid dada \mid d,则称 ddaabb公倍数

所有公倍数中最小的被称为 a,ba,b最小公倍数,记做 lcm(a,b)\operatorname{lcm}(a,b).

性质#

  1. am,bma \mid m,b \mid m,则gcd(a,b)m\gcd(a,b) \mid m.

  2. da,dbd \mid a,d \mid b,则 dlcm(a,b)d \mid \operatorname{lcm}(a,b).

  3. lcm(a,b)=abgcd(a,b)\operatorname{lcm}(a,b)=\dfrac{ab}{\gcd(a,b)}.

  4. m,a,bm,a,b 是正整数,则 lcm(ma,mb)=m×lcm(a,b)\operatorname{lcm}(ma, mb)=m\times \operatorname{lcm}(a, b).

  5. mm 是非零整数 a1,a2,a3,,ana_1,a_2,a_3,\cdot\cdot\cdot, a_n 的公倍数,则 lcm(a1,a2,  ,an)m\operatorname{lcm}(a_1, a_2,\cdot\cdot\cdot\;, a_n) \mid m.

欧几里得算法#

欧几里得算法,又名辗转相除法

有结论:

gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b,a\bmod b)

a=qb+r,(r<q)a=qb+r,(r<q)dad\mid adbd\mid b.

由于 a=qb+ra=qb+r,故 r=aqbr=a-qb

另一方面,由于 dad\mid adbd\mid b,有 daqbd\mid a-qb.

于是得到 drd\mid r,所以任意 a,ba,b 的公因数都是 b,rb,r 的公因数,r<ar<a,所以 gcd(a,b)=gcd(b,r)\gcd(a,b)=\gcd(b,r) 得证。

时间复杂度为 O(logn)\mathcal{O}(\log n),下面是代码实现。

int gcd(int a, int b){
	if(b == 0) return a;
    return gcd(b, a % b);
}
cpp

筛法#

埃氏筛法#

**问题:**给定 nn,预处理出每个 [1,n][1,n] 范围内的整数是否为质数的算法。

思路:从小到大枚举,若确定 xx 为质数,则将 x,2x,3x,,kxx, 2x, 3x, \cdots, kx 标记为合数。若遍历到 xx 时,其仍然未被标记为合数,则可确定 xx 为质数。

还有以下优化:

  1. jj 的初值改为 j=iij=i\cdot i,因为对于 j=ik,(k<i)j=i\cdot k,(k < i),合数 jj 会被 kk 提前筛掉。
  2. ii 枚举的范围为 [2,n][2,\sqrt{n}],因为对于 i>ni>\sqrt{n}i2>ni^2>n,起不到任何贡献。
for(int i = 2; i <= n; i++) is_prime[i] = true;
for(int i = 2; i * i <= n; i++) {
    if(is_prime[i])
		for(int j = i * i; j <= n; j += i) is_prime[j] = false;
}
cpp

经过简单优化的埃氏筛,可证明时间复杂度为 O(nloglogn)\mathcal{O}(n\log \log n),相较于 O(n)\mathcal{O}(n) 的线性筛,埃氏筛并不实用。

欧拉筛法#

欧拉筛法,又称线性筛,顾名思义,其时间复杂度为 O(n)\mathcal{O}(n)

思路:在埃氏筛法中,每个合数可能会被筛多次,若每个合数恰好被筛一次,时间复杂度就是线性了。在线性筛中,每一个合数都是被最小的质因子筛掉,不过这在常见的代码实现中并不显然。

int flag[N + 10], pri[N + 10], cnt = 0;
inline void Euler() {
	flag[1] = 1;
	for(int i = 2; i <= N; i++) {
		if(!flag[i]) pri[++cnt] = i;
		for(int j = 1; i * pri[j] <= N && j <= cnt; j++) {
			flag[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
		}
	}
}
cpp

线性筛莫比乌斯函数#

μ(n)={1n=10n 含有平方因子(1)kk 为 n 的本质不同质因子个数 \mu(n)= \begin{cases} 1&n=1\\ 0&n\text{ 含有平方因子}\\ (-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\ \end{cases}

莫比乌斯函数是积性函数。

int flag[N + 10], pri[N + 10], mu[N + 10], cnt;
mu[1] = 1;
for(int i = 2; i <= N; i++) {
    if(!flag[i]) pri[++cnt] = i, mu[i] = -1;
    for(int j = 1; j <= cnt && i * pri[j] <= N; j++) {
        flag[i * pri[j]] = 1;
        if(i % pri[j] == 0) break;
        else mu[i * pri[j]] = -mu[i];
    }
}
cpp

观察线性筛的过程,当 imodprij=0i\bmod \operatorname{pri}_j=0 时,priji(prij)2iprij\operatorname{pri}_j\mid i \Rightarrow (\operatorname{pri}_j)^2\mid i\cdot \operatorname{pri_j},这种情况下 μ(iprij)=0\mu(i\cdot \operatorname{pri_j})=0.

否则,prij\operatorname{pri_j} 是第一次对 ipriji\cdot \operatorname{pri_j} 产生贡献,μ(iprij)=μ(i)\mu(i\cdot \operatorname{pri_j})=-\mu(i).

欧拉函数与欧拉定理#

定义#

给定 nNn\in \Bbb{N}^{*},欧拉函数是小于等于 nn 的所有数中与 nn 互质的数的个数,记为 φ(n)\varphi(n).

形式化地,有:

φ(n)=i=1n[gcd(i,n)=1]\varphi(n)=\sum_{i=1}^{n} [\gcd(i,n)=1]

积性函数#

积性函数:满足 f(mn)=f(m)f(n)f(m\cdot n)=f(m)\cdot f(n)gcd(m,n)=1\gcd(m,n)=1.

完全积性函数:满足 f(mn)=f(m)f(n)f(m\cdot n)=f(m)\cdot f(n),不要求 m,nm,n 互质。

可以证明,欧拉函数是积性函数

求单个数的欧拉函数#

有结论:

φ(x)=x(11p1)(11p2)(11pn)\varphi(x)=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_n})

其中 p1,p2,,pnp_1,p_2,\cdots,p_nxx 的所有质因数,例如:φ(12)=12×(112)×(113)=4\varphi(12)=12\times(1-\frac{1}{2})\times(1-\frac{1}{3})=4.

推导过程如下。

首先,显然 φ(1)=1\varphi(1)=1,且若 pp 是质数,有 φ(p)=p1\varphi(p)=p-1.

给定质数 pp 和正整数 kk,考虑计算 φ(pk)\varphi(p^k)

根据质数的性质,只有 pp 的倍数与 pkp^k 不互质,因此一种计算方法是:用总数 pkp^k 减去 pp 的倍数个数,即:

φ(pk)=pkpk1=pk×(11p)\varphi(p^k)=p^k-p^{k-1}=p^k\times (1-\frac{1}{p})

根据积性函数性质可得:

φ(x)=φ(p1k1)×φ(p2k2)××φ(pnkn)=p1k1(11p1)p2k2(11p2)pnkn(11pn)=x(11p1)(11p2)(11pn)\begin{aligned} \varphi(x)&=\varphi(p_1^{k_1})\times\varphi(p_2^{k_2})\times\cdots\times\varphi(p_n^{k_n})\\ &=p_1^{k_1}(1-\frac{1}{p_1})p_2^{k_2}(1-\frac{1}{p_2})\cdots p_n^{k_n}(1-\frac{1}{p_n})\\ &=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_n}) \end{aligned}
inline int phi(int n) {
	int res = n;
	for(int i = 2; i * i <= n; i++) { // 分解质因数
		if(n % i == 0) { // i 是 n 的质因子
			res = res / i * (i - 1);
			while(n % i == 0) n /= i;
		}
	}
	if (n > 1) //最后可能还剩一个最大的质因子
		res = res / n * (n - 1);
	return res;
}
cpp

欧拉定理#

内容#

任意选取 a,nNa,n\in \Bbb{N}^{*} 满足 gcd(a,n)=1\gcd(a, n)=1,则有:

aφ(n)1(modn)a^{\varphi(n)}\equiv1\pmod n

证明#

费马小定理#

对任意给定的正整数 aa 和质数 pp,都有:

ap11(modp)a^{p-1}\equiv 1\pmod p

容易发现,这是欧拉定理的特殊情况,证明不再赘述。

扩展欧几里得算法#

概述#

扩展欧几里得算法,用于求解一元一次不定方程 ax+by=gcd(a,b)ax+by=\gcd(a,\,b),其中 a,ba,b 为已知系数,x,yx,y 为未知数。

裴蜀定理#

a,ba,b 是不全为零的整数,则存在整数 x,yx,y 使得 ax+by=gcd(a,b)ax+by=\gcd(a, b).

因此,扩展欧几里得算法所求解的不定方程一定有整数解。

特解#

要求出一组满足方程条件的整数解 (x,y)(x,y).

考虑欧几里得算法的过程,gcd(a,b)=gcd(b,amodb)\gcd(a, b)=\gcd(b, a\bmod b).

d=gcd(a,b)d=\gcd(a, b),则要求解的方程为 ax+by=dax+by=d.

关注欧几里得算法的递归过程(a,b)(a,b) 递归至 (b,amodb)(b, a\bmod b) 后,有方程 bx1+(amodb)y1=db\cdot x_1+(a\bmod b)\cdot y_1=d.

现在要找到 (x,y)(x,y)(x1,y1)(x_1,y_1) 之间的等量关系,即:从递归的深层推到浅层

根据 mod\bmod 运算的定义:

bx1+(amodb)y1=dbx1+(aab×b)y1=db\cdot x_1+(a\bmod b)\cdot y_1=d \Rightarrow b\cdot x_1+(a-\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1=d

整理得到:

d=bx1+(aab×b)y1=bx1+ay1(ab×b)y1=ay1+b(x1ab×y1)\begin{aligned} d&=b\cdot x_1+(a-\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1\\ &=b\cdot x_1+a\cdot y_1-(\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1\\ &=a\cdot y_1+b\cdot(x_1-\left\lfloor\frac{a}{b}\right\rfloor\times y_1) \end{aligned}\\

与方程 ax+by=dax+by=d 对应,可知一种可行的对应关系为:

{x=y1y=x1ab×y1\left\{ \begin{array}{l} x=y_1\\ y=x_1-\left\lfloor\frac{a}{b}\right\rfloor\times y_1 \end{array} \right.

按照这样的等量关系,在递归过程中计算即可。

欧几里得算法的递归基为 b=0b=0,此时 gcd(a,b)=a\gcd(a,b)=a,方程 ax+by=dax+by=d 的解为 x=0,y=1x=0,y=1.

int exgcd(int a, int b, int &x, int &y) { // 函数返回值为gcd(a,b)
	if(b == 0) {
		x = 1, y = 0;
		return a;
	}
	int r = exgcd(b, a % b, x, y); //先递归
	int t = x; //再计算x,y
	x = y;
	y = t - a / b * y;
	return r;
}
cpp

可以证明,这种方法求出 ax+by=dax+by=d 的特解 (x,y)(x,y) 必满足 xb|x|\leq bya|y|\leq a.

通解#

假设已求出特解为 (x0,y0)(x_0,y_0),满足 ax0+by0=gcd(a,b)ax_0+by_0=\gcd(a,\,b).

仍然记 d=gcd(a,b)d=\gcd(a, b),则有

a(x0+kbd)+b(y0kad)=d,kZa(x_0+k\frac{b}{d})+b(y_0-k\frac{a}{d})=d,k\in \Bbb{Z}

显然,kbdk\dfrac{b}{d}kadk\dfrac{a}{d} 是在保证解仍为整数的前提下,可取到的最小整系数

所以方程的通解为:

{x=x0+kbdy=y0kad\left\{ \begin{array}{l} x=x_0+k\frac{b}{d}\\y=y_0-k\frac{a}{d} \end{array} \right.

对于任意二元一次不定方程 ax+by=c,(a,b,cZ)ax+by=c,(a,b,c\in \Bbb{Z}),当且仅当 dcd\mid c 时才有整数解。

在求出 ax+by=gcd(a,b)ax+by=\gcd(a,b) 后,可以得到方程特解为 (cdx0,cdy0)(\frac{c}{d}x_0, \frac{c}{d}y_0),且有通解为:

{x=cdx0+kbdy=cdy0kad\left\{ \begin{array}{l} x=\frac{c}{d}x_0+k\frac{b}{d}\\y=\frac{c}{d}y_0-k\frac{a}{d}\end{array} \right.

最小非负整数解#

由通解形式,可用 (x % (b/d) + (b/d)) % (b/d) 求得最小非负整数解。

这是因为在 C++ 语言中,负整数 xx 进行运算 x % p 的结果满足 p<s0-p<s\leq 0.

同余方程#

同余方程,指形如 axb(modc)ax\equiv b\pmod c 的方程。其中 a,b,ca,b,c 是已知量,xx 是未知量。

注意到:

axb(modc)xa+kc=bax\equiv b\pmod c\Leftrightarrow xa+kc=b

于是转化为一般的一元一次不定方程,可用扩展欧几里得算法求解。

乘法逆元#

定义#

给定两个整数 aapp,假设存在 xx 使得 ax1(modp)ax\equiv 1\pmod p,则称 xxaa 关于 pp乘法逆元

乘法逆元又称数论倒数,是模意义下的除法,可记为 xa1(modp)x\equiv a^{-1}\pmod p.

可以证明,aa 关于 pp 的逆元存在的充要条件是 gcd(a,p)=1\gcd(a,p)=1.

求一个数的逆元#

将上面式子变形,变成ax+kp=1ax+kp=1,用扩展欧几里得能够求出一个xx.

pp 是质数,可利用费马小定理求逆元。

ap1modp=(ap2×a)modp=(x×a)modp=1a^{p-1}\bmod p=(a^{p-2}\times a)\bmod p=(x\times a)\bmod p=1

所以 aa 关于 pp 的逆元是 ap2modpa^{p-2}\bmod p 的值,用快速幂优化,时间复杂度为 O(logn)\mathcal{O}(\log n).

代码实现不难。

求一组数的逆元#

给定 a1,a2,,ana_1, a_2, \cdots, a_n 和模数 pp,若他们都与 pp 互质,则可以在 O(n+logai)\mathcal{O}(n+\log a_i) 的时间复杂度内计算出每个数的逆元。

预处理前缀积 si=(i=1iai)modp\operatorname{s}_i=(\prod_{i=1}^i a_i) \bmod p,先计算出前缀积数组的逆元 tit_i.

tnt_n 可通过扩展欧几里得算法或快速幂计算得到,对于 t1n1t_{1\sim n-1},可以递推计算:

在模意义下,有:

ti1a1a2ai(modp)ti+11a1a2aiai+1(modp)t_i\equiv \frac{1}{a_1a_2\cdots a_i}\pmod p\\ t_{i+1}\equiv\frac{1}{a_1a_2\cdots a_i a_{i+1}}\pmod p

所以:

ti1a1a2aiai+1a1a2aiai+1ti+1ai+1(modp)t_i\equiv \frac{1}{a_1a_2\cdots a_i}\equiv \frac{a_{i+1}}{a_1a_2\cdots a_i a_{i+1}}\equiv t_{i+1}\cdot a_{i+1}\pmod p\\

求出 tit_i 后,设 aia_i 的逆元为 invi\operatorname{inv_i},则 inv1=t1\operatorname{inv}_1=t_1,其余项可以直接计算:

invia1a2ai1a1a2ai1aitisi1(modp)\operatorname{inv}_i\equiv \frac{a_1a_2\cdots a_{i-1}}{a_1a_2\cdots a_{i-1}a_i}\equiv t_i\cdot s_{i-1}\pmod p

组合数取模#

按照上面的方法,代入 ai=ia_i=i,求出 si=i!s_i=i!ti=si1=1i!t_i={s_i}^{-1}=\dfrac{1}{i!}.

根据组合数的定义:

Cnm(nm)n!m!(nm)!sntmtnm(modp)C_n^m\equiv \binom{n}{m}\equiv \frac{n!}{m!(n-m)!}\equiv s_n\cdot t_m\cdot t_{n-m}\pmod p
const int N = 1e6, mod = 1e9 + 7;
inline int power(int x, int k) {
	int ret = 1;
	while(k) {
		if(k & 1) ret = ret * x % mod;
		x = x * x % mod, k >>= 1;
	}
	return ret;
}
int fac[N + 10], inv[N + 10];
inline void init() {
	fac[0] = 1;
	for(int i = 1; i <= N; i++) fac[i] = fac[i - 1] * i % mod;
	inv[N] = power(fac[N], mod - 2);
	for(int i = N - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
cpp

中国剩余定理#

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

这个问题就可以转化为一个同余方程组。设 xx 为物品数量,则有:

{x2(mod3)x3(mod5)x2(mod7)\left\{ \begin{align}{} x\equiv 2\pmod 3\\ x\equiv 3\pmod 5\\ x\equiv 2\pmod 7 \end{align}{} \right.

内容#

中国剩余定理一元同余线性方程组:

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

假设整数 m1,m2,,mnm_1,m_2,\cdots,m_n 两两互质,则方程组必有解,通解可以构造如下:

M=miM=\prod m_i,并设 Mi=MmiM_i=\dfrac{M}{m_i}tiMi1(modmi)t_i\equiv M_i^{-1}\pmod {m_i},有通解为:

x=a1t1M1+a2t2M2++antnMn+kM=i1naitiMi+kMx=a_1t_1M_1+a_2t_2M_2+\cdots+a_nt_nM_n+kM=\sum_{i-1}^{n}a_it_iM_i+kM

这不难理解。在 modmi\bmod m_i 意义下,整数 jj 满足:

ajtjMj{0,jiaj,j=i(modmi)a_jt_jM_j\equiv \left\{ \begin{array}{} 0, &j\neq i\\ a_j, &j=i \end{array} \right. \pmod {m_i}

因为 jij\neq i 时,Mj0(modmi)M_j\equiv 0\pmod {m_i},且有 tiMi1(modmi)t_iM_i\equiv 1\pmod {m_i}.

代码实现#

程序的返回值为最小整数解。

int exgcd(int a, int b, int &x, int &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	int r = exgcd(b, a % b, x, y);
	int t = x;
	x = y;
	y = t - a / b * y;
	return r;
}
int a[101], m[101];
inline int CRT(int n) {
	int M = 1;
	int ans=0;
	for(int i = 1; i <= n; i++) {
		M *=m [i];
	}
	for(int i = 1; i <= n; i++) {
		int x, y;
		int Mi = M / m[i];
		exgcd(Mi, m[i], x, y);
		ans = (ans + Mi * x * a[i]) % M;
	}
	if(ans < 0) ans += M;
	return ans;
}
cpp

扩展中国剩余定理#

概述#

同样用于求解同余方程组,与中国剩余定理的区别是模数不一定互质

内容#

考虑两个同余方程:

{xa1(modm1)xa2(modm2)\left\{ \begin{aligned}x\equiv a_1 \pmod{m_1}\\ x\equiv a_2\pmod{m_2} \end{aligned} \right.

可以写为:

{x+k1m1=a1(1)x+k2m2=a2(2)\left\{ \begin{array}{} x+k_1m_1=a_1&&(1)\\ x+k_2m_2=a_2&&(2) \end{array} \right.

(1)(2)(1)-(2) 得到:

m1k1m2k2=a1a2m_1k_1-m_2k_2=a_1-a_2

可见,方程形如 ax+by=cax+by=c,所以有解条件是 gcd(m1,m2)(a1a2)\gcd(m1,m2)\mid (a_1-a_2).

用扩展欧几里得算法求得可行解 (K1,K2)(K_1, K_2),因此 K1K_1 的通解为:

K1+m2dCK_1+\frac{m_2}{d}\cdot C

代入 (1)(1),得到:

x+K1m1+m2m1dC=a1x+K_1m_1+\frac{m_2m_1}{d}\cdot C=a_1

写成同余方程的形式:

xa1K1m1(modlcm(m1,m2))x\equiv a_1-K_1m_1 \pmod{\operatorname{lcm}(m_1, m_2)}

这样就把两个同余方程合并成了一个。

同理,可以将 nn 个方程组成的同余方程组化为一个同余方程,最后用扩展欧几里得算法求解 xx.

卢卡斯定理#

对于质数 pp,有:

(nm)modp=(npmp)(nmodpmmodp)modp\binom{n}{m}\bmod p = \binom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor} \cdot \binom{n\bmod p}{m\bmod p}\bmod p

p106p\leq 10^6n,mn,m 较大时实用。

扩展卢卡斯定理#

123

数论基础
https://www.tonyyin.top/blog/oi-notes/number-theory
Author TonyYin
Published at March 8, 2022
Comment seems to stuck. Try to refresh?✨