[算法学习]模板-基本数论
底层数学与算力降维系统复盘:从暴力死循环到 O(1) 极速查表
在系统学习底层数学 API 之前,我对它的理解只是:
“数学题就是背公式,找素数就是写个 for 循环一直除。”
但真正深入后发现,它其实是一种极限压榨 CPU 吞吐量的降维思想。
这些数学模板的本质是:
用极小的时间复杂度代价(预处理/二进制拆分),
利用数学定理或物理内存,
把 $O(N)$ 甚至 $O(N^2)$ 的死循环压缩成 $O(\log N)$ 乃至 $O(1)$。
理解它的关键,不是背诵干瘪的代码,而是理解:
- 我们在消灭什么重复计算?
- 为什么巨大的指数可以拆分成二进制?
- 为什么应对几十万次的查询不会 TLE(超时)?
一、为什么底层数学能优化算力?
我们先看一个找素数的典型暴力结构:
1 | |
这个结构做了什么?
- 每次查询都重新从头算一遍
- 遇到多次重复的数字,依然傻傻地再算一次
但很多数学题中:
- 数据的状态是固定的(比如 7 永远是素数)
- 或者乘法的步骤存在极其严重的冗余
一旦计算步骤具有“高度重复性”和“可拆解性”,我们就可以:
将“计算”转化为“查表”或“位运算”。
二、算力降维武器分类
底层数学与防爆破技巧主要分为三个核心模块:
- 埃氏筛与前缀和(空间换时间,极致查表)
- 快速幂(二进制切割,指数降维)
- 游标跳跃($K+1$ 抽屉原理,斩断冗余遍历)
第一部分:埃氏筛与前缀和
1️⃣ 思想核心
不要等询问来了再去算,而是在系统启动时:
一次性把所有状态标好,以后永远只查表。
2️⃣ 从暴力到优化:完整推理过程
暴力试除法:
每个数字都去循环除以 $2$ 到 $\sqrt{N}$。如果有 $M$ 次查询,时间复杂度直接原地爆炸。
为什么可以优化?
因为合数一定是由较小的素数乘出来的。
只要我们从 2 开始,把每一个素数的所有倍数全部提前标记为合数,那么剩下的,绝对就全是素数。
搭配上前缀和数组,我们可以将区间 $[L, R]$ 之间的素数个数统计压缩到真正的 $O(1)$ 极速查询。
3️⃣ 标准模板 (防爆破版)
1 | |
第二部分:快速幂魔法
1️⃣ 思想核心
求 $A^B \pmod P$。
把十进制的巨大指数 $B$,拆解成计算机底层的二进制。
1 | |
2️⃣ 从暴力到优化推理
暴力相乘:乘 $B$ 次。时间复杂度 $O(N)$。
但我们可以利用拼图思想:
$$A^{11} = A^8 \times A^2 \times A^1$$
我们只需要让 base 不断自乘(平方升级),然后利用位运算 B & 1 检查当前二进制位是否为 1。如果是,就把这块拼图乘进答案里。
这把几亿次的循环,瞬间降维到了几十次。
3️⃣ 标准模板 (全员 long long 防护)
1 | |
第三部分:GCD 与 LCM (公约数/公倍数)
1️⃣ 思想核心与数学本质
极其优美的一行递归与一个绝对真理定理。
绝对真理:
$$A \times B = GCD \times LCM$$
只要有了 GCD,LCM 直接通过移项公式 $O(1)$ 得出。
2️⃣ 标准模板
1 | |
第四部分:降维打击实战 —— K+1 游标跳跃
(注:源自 AtCoder 真实 TLE 血泪史)
1️⃣ 从暴力到优化的极限跨越
要在 30 万个数字里,经历 20 万次“拿走 $K$ 个数字,求剩下数字的最小值”的查询。
暴力遍历:
每次查询都要刷一遍数组(memset)并重新遍历 30 万个球,总运算量 $600$ 亿次 -> 必死 TLE。
$K+1$ 抽屉原理优化:
因为每次最多拿走 $K$ 个球,只要我们事先对全局排好一次序。
每次查询,我们搞一个“游标”从前往后看。就算排在最前面的 $K$ 个最小球全被拿走了,游标最多只需要往后跳 $K$ 步,第 $K+1$ 个球绝对还在袋子里,且绝对是全局最小值!
2️⃣ 标准模板结构
1 | |
三、暴力 vs 降维 本质区别
| 业务场景 | 暴力流 (The Stupid Way) | 降维流 (The Hacker Way) | 核心工具 |
|---|---|---|---|
| 素数查询 | $O(N \sqrt{N})$ 循环试除 | $O(1)$ 极速查表 | 埃氏筛 + 前缀和 |
| 巨大指数 | $O(N)$ 乘到内存爆破 | $O(\log N)$ 二进制拼图 | &1 与 >>=1 |
| 动态最小值 | 每次 memset + $O(N)$ 遍历 |
全局排序 + 游标跳跃 $K$ 步 | 结构体排序 + 状态重置 |
四、如何判断是否能用这些降维魔法?
问自己三件事:
1️⃣ 题目是否有大量的重复区间查询?
→ 是,优先考虑预处理建表(前缀和/素数筛)。
2️⃣ 指数是否大到离谱(比如 $10^9$)?
→ 是,无脑套快速幂。
3️⃣ 题目中的动态修改,是否有总量的微小限制(如总操作数 $K$ 很小)?
→ 是,优先考虑全局排序 + 游标局部跳跃,千万别从头暴力遍历。
五、底层算力优化的真正的本质
降维打击不是背诵复杂的代码结构。
本质是:
不做任何多余的运算。
暴力是:
无论刚才有没有算过,全当没看见,从头再算一遍。
算力优化是:
- 算过的结果,存在数组里直接查。
- 无关的数字,利用单调性直接跳过。
- 物理的极限,用数学定理直接绕开。
这是一场:
对 CPU 资源的极致榨取。
六、我对算力降维的阶段性理解
最开始:
- 觉得只要代码能出正确答案就行。
后来发现:
- 在 10 万级别的数据面前,跑不进 1 秒的正确代码等于废代码。
真正理解后:
- 算法的瓶颈从来不是代码写得多长,而是时间复杂度这堵绝对的物理高墙。
七、总结
数学 API(素数/快速幂/GCD):
用底层规律打破 $O(N)$ 的壁垒。
$K+1$ 游标跳跃:
用提前排序消灭冗余遍历。
两者共同核心:
永远在思考如何“降维”。