OI退役笔记-001:数论(一)基础(基础知识、前缀和、差分、快速幂、快速乘)

一、整除

a, b ∈ Z,且 b ≠ 0,若存在 c ∈ Z,使得 b = ac,则称 b 被 a 整除,a 整除 b,记作 a | b。又称 b 是 a 的倍数,a 是 b 的约数。

性质
设 a, b, c ∈ Z,

  1. 若 a | b 且 b | a,则 a = ±b
  2. 若 a | b 且 b | c,则 a | c
  3. 若 a | bc,且 (a, b) = 1,则 a | c
  4. 若 a | b,且 a | c,则 a | (b, c),a | bx + cy(x, y ∈ Z)
  5. a | c 且 b | c,则 [a, b] | c;当 (a, b) = 1 时,ab | c
    [注释]:数论中,(a, b) 表示 gcd(a, b);[a, b] 表示 lcm(a, b)。(小学奥数里好像也有)
    以上性质的正确性证明略。

二、带余除法

对于任意的 a, b ∈ Z(设 a ≥ b 且 b ≠ 0),存在唯一的商 q 和余数 r,使得 a = bq + r(其中 0 ≤ r ≤ |b|)。
注意:该定理包括负数。
(小学应该就会了)

三、模运算

在 C++ 中,% 表示取模,a % b 返回 a 除 b 的余数。
性质

  1. 加法:(a + b) % p = (a % p + b % p) % p
  2. 减法:(a - b) % p = (a % p - b % p) % p
  3. 乘法:(a * b) % p = ((a % p) * (b % p)) % p
  4. 幂:(ab) % p = ((a % p)b) % p
  5. 模运算满足结合律、交换律和分配律

以上性质的正确性证明均可采用设 a = k1p + r1,b = k2p + r2,0 ≤ r1, r2 ≤ |p| 的方法证明。

同余
有两个整数 a, b,若它们除以正整数m所得的余数相等,则称 a, b 对于模 m 同余,记作:

a ≡ b (mod m) 

若 a, b 同余,则 m | a - b。

同余的性质

  1. 反身性:a ≡ a (mod m)
  2. 对称性:若 a ≡ b (mod m),则b ≡ a (mod m)
  3. 传递性:若 a ≡ b (mod m),b ≡ c (mod m),则a ≡ c (mod m)
  4. 同余式相加减:若 a ≡ b (mod m),c ≡ d (mod m),则a ± c ≡ b ± d (mod m)
  5. 同余式相乘:若 a ≡ b (mod m),c ≡ d (mod m),则 a * c ≡ b * d (mod m)

以上性质的正确性证明略。

四、前缀和与差分

前缀和
前缀和用于以 O(1) 的时间复杂度求一个区间内所有数的和。

// 初始化
for (int i = 1; i <= n; ++i)
	sum[i] = a[i] + sum[i - 1];

// 求解 [i, j] 中所有数的和
ans = sum[j] - sum[i - 1];

进阶:
【二维前缀和】
根据容斥原理,可知:

for (int i = 1; i <= n; ++i)
	for (int j = 1; j <= n; ++j)
		sum[i][j] = a[i][j] + sum[i - 1][j] + sum[i][j - 1]
					- sum[i - 1][j - 1];

// 求解 a[x][y] 到 a [z][w]:
ans = sum[z][w] - a[x - 1][y] - a[x][y - 1] + a[x - 1][y - 1];

不明白的看一下:
S = S4 + S2 + S3 - S1
容斥原理
差分
类似前缀和,用于维护一个被不断修改的区间。
简单来说,有多次操作使 [a, b] 内的每个数都 ±w。

// 初始化
for (int i = 1; i <= n; ++i)
	diff[i] = a[i] - a[i - 1];

// [i, j] 中所有数加 w
diff[i] += w; diff[j + 1] -= w;

// [i, j] 中所有数减 w
diff[i] -= w; diff[j + 1] += w;

// 将 diff 数组还原为 a 数组
for (int i = 1; i <= n; ++i)
    a[i] = diff[i] + a[i - 1];

可以这么想:diff 数组中第 i 个数存的是 a[i] 与a[i - 1] 的差值,若 a[i] ~ a[j] 每个数加上 w,则直接 a[i] += w 即可,因为 diff 数组记录的都是后一个数减前一个数的值,a[i] 相当于比之前的 a[i] 大 w,则 a[i + 1] 比 a[i] 大 diff[i + 1] + w,但还是比新的 a[i + 1] 大 diff[i + 1]。

五、快速幂与快速乘

先讲快速幂:
1.快速幂原理

快速幂算法的核心思想就是每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。

以 211 为例,其等于2 ^ ( 20 + 21 + 23)。
若从 11 逆推,则:

用 r 表示结果; 用 base 表示底数。

  1. r = 1,base = a
  2. 11 % 2 = 1,11 / 2 = 5
  3. r *= base = base 1,base = base * base = base2
  4. 5 % 2 = 1,5 / 2 = 2
  5. r *= base = base 3,base = base * base = base4
  6. 2 % 2 = 0,2 / 2 = 1
  7. r *= base = base 7 base = base * base = base8
  8. 1 % 2 = 1,1 / 2 = 0
  9. r *= base = base 11,base = base * base = base16(第二步不在运算范围内)
  10. 此时幂数为 0,应乘以 1(省略),直接返回

Δ 时间复杂度为O(log₂N)

2.快速幂实现
需加语句、头文件:

typedef long long ll;

快速幂常规算法:

ll qPow(ll a, ll b)
{
	ll r = 1, base = a;
	while (b)
	{
		if (b & 1)				// 若 b 为奇数,则 b & 1 == 1;若 b 为偶数,则 b & 1 == 0
			r = r * base;
		base = base * base;
		b >>= 1;				// 相当于 b /= 2;同理,b <<= 1 相当于 b *= 2
	}
	return r;
}

快速幂取模算法:

ll qmPow(ll a, ll b, ll m)
{
	ll r = 1, base = a % m;
	while (b)
	{
		if (b & 1)
			r = (r * base) % m;
		base = (base * base) % m;
		b >>= 1;
	}
	return r;
}

2.快速乘实现
快速乘与快速幂思想一样,代码类似,只不过将乘换成了加

ll qmMul(ll a, ll b, ll m)
{
	ll r = 0, base = a % m;
	while (b)
	{
		if (b & 1)
			r = (r + base) % m;
		base = (base + base) % m;
		b >>= 1;
	}
	return r;
}

作者:Rotch
日期:2020-09-24
修改:2020-05-12(五稿)
[2020-09-24]:修正了一些错误
[2020-10-01]:用角标替换 ^
[2021-05-03]:修正了一些错误,加入了快速乘部分
[2020-05-12]:更改为数论基础

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值