信息学奥赛算法练功房
从递归开始,一路到线段树和最短路。每一章都走同一条路: 先写暴力,亲眼看它慢;找出慢在哪;只讲清楚关键的那一步;再写正解; 最后拿暴力当标准答案,随机对拍验证你自己写的那份。
这个站怎么用
- 每章必须白纸默写一遍正解,不许对着答案抄。 写不出来说明「关键的一步」没真懂 —— 回去重看那一段,不要往下走。 看懂和写得出之间隔着一条鸿沟,只有默写能测出来。
- 用对拍器验证自己写的,而不是对答案。对答案只会告诉你「不一样」;对拍会告诉你「哪一组数据不一样」, 而且那组数据小到你能在纸上手推。这是竞赛选手真正的调试方式。
- 暴力解永远先写。它是你理解题意的手段、是对拍的标准答案、是考场上骗分的保底。 跳过暴力直奔正解,是自学最容易走死的一条路。
每一章都走同一条主线
这个结构本身就是学习方法。看章节时留意自己卡在哪一步 —— 卡在「关键的一步」是正常的(那里才是真正的难点), 卡在「手算一遍」说明题意还没读懂,回去重读第 1 步。
- 一句话问题(例子很小,能手算)
- 先用纸笔手算一遍,建立直觉
- 暴力解法(完整可运行,而且它就是后面对拍的标准答案)
- 实测暴力有多慢 —— 把规模调大,亲眼看它卡死
- 慢在哪:找出重复计算或无效搜索的具体位置
- ★ 关键的一步 —— 全章唯一真正需要想明白的东西
- 正解代码 + 单步动画(每一章都有)
- ★ 对拍验证:把你自己写的贴进去,随机数据自动查错
- 自测清单 + 配套练习
越往后,还会多出这几样
所以章节篇幅是越走越长的(前面 9 步,后面到 17 步)—— 多出来的这些不是凑数,它们是前面那条主线走到后期时暴露出来的问题。下面这四样,才是这套教材真正想教的东西。
- ★ 换尺子 —— 秒表看不见的东西太多了(差别小、机器抖、缓存作祟)。 于是改成数「碰了多少个格子」「跳了多少步」「比较了多少次」: 这些数是算得出来的,能和证明对上,也不随机器变。
- ★ N 种把它写错的方式 —— 每一种都写成一份能跑的错误代码, 并说清它靠什么现形;有几种是对拍原理上抓不到的,那就得换尺子。
- ★★ 生成器调了七次,每次只改一处 —— 同一个 bug,在「顺手写的」数据上 300 轮一次都抓不到,把某一处跨过一条具体的线之后立刻 278/300。造数据比算法本身更容易骗过你,这是全书重复最多的一课。
- 还第 N 章的账 —— 前面章节末尾预告过的对比和解法, 到该还的时候原样兑现(而且跨章节互相对拍,生成器都不换)。
关于「运行」按钮
点运行是真的调用你电脑上的 g++ 编译执行,不是网页模拟,所以你能看到真实耗时, 也能随便改代码重跑。用 npm run dev 启动时,网站和本地运行服务会一起起来。
如果只跑了 npm run web,或者打开的是 npm run build 出来的静态版本, 运行按钮会自动变成「复制代码」—— 讲解、动画、代码照样能看,只是不能当场跑。 这样这份站点也可以直接发给学生。
学习路线:53 章
覆盖 CSP-J 普及组全部考点 + CSP-S 提高组常见考点。这 53 章已经全部写完 —— 每一章都带动画,都能当场编译运行、随机对拍。
阶段 0 · 递归思维
- 1递归入门:函数怎么调用自己 递归三要素 · 调用栈动画可学
- 2递归的分解思维:汉诺塔与斐波那契 把大问题切成同形状的小问题 · 汉诺塔动画可学
- 3递归 = 决策树:子集、组合、全排列 每步选或不选 · 决策树动画可学
- 4回溯与状态恢复:N 皇后 进入 → 递归 → 撤销 · 棋盘回溯动画可学
阶段 1 · 基础技巧
阶段 2 · 排序与分治
- 10排序:冒泡 → 归并 → 快排 sort 与自定义 cmp 的正确写法 · 冒泡/归并动画可学
- 11分治 归并排序顺手把逆序对数出来 · 合并时顺手数 · 一批一批地数动画可学
- 12分治进阶 跨越中点 · 只走一边(减治)· 快速选择动画可学
阶段 3 · 搜索
- 13DFS 深度优先搜索:网格连通块 一条道走到黑 · 染色动画可学
- 14BFS 广度优先搜索:迷宫最短路 第一次到达即最短 · 扩散动画可学
- 15BFS 变形:多源 BFS 与状态图搜索 八数码 · 多源扩散动画可学
- 16DFS 剪枝:可行性、最优性、搜索顺序 三板斧 · 剪枝开关动画可学
- 17记忆化搜索 从递归通向 DP 的那座桥可学
- 18迭代加深与双向 BFS 省内存 / 砍一半深度 · 单向vs双向动画提高组 S可学
阶段 4 · 贪心
- 19贪心基础:排序型贪心 排队接水 · 区间调度 · 交换论证动画可学
- 20贪心的正确性:交换论证 + 用对拍打假错误贪心 找零钱 · 01 背包 · 三台打假用的对拍器可学
阶段 5 · 动态规划
- 21DP 入门:从记忆化到递推 爬楼梯 · 数字三角形 · 填表顺序动画可学
- 22线性 DP:最长上升子序列 O(n²) → O(n log n) · tails 动画可学
- 2301 背包 二维 → 滚动 → 一维倒序 · 「同一件物品被拿三次」动画可学
- 24完全背包与多重背包 正序 vs 倒序互为反面 · 二进制拆分动画可学
- 25二维费用与分组背包 多一维就多一层循环 · 三层循环顺序动画提高组 S可学
- 26区间 DP:石子合并 按区间长度填表 · 三角形表动画 · 打假「先合最小两堆」提高组 S可学
- 27树形 DP:没有上司的舞会 后序遍历 = 依赖谁先填谁 · 树上 DFS 动画 · 一个藏在编号里的 bug提高组 S可学
- 28状压 DP 入门:旅行商问题 集合就是一个整数 · 状态表填格动画 · 一个藏在对称性里的 bug提高组 S可学
阶段 6 · 图论
- 29图的存储:三种存法的对比与选型 三个内存公式 · 扫一遍 n² vs 2m 动画 · 一个顺手写法漏掉三个 bug可学
- 30图上的 DFS 与 BFS、连通性 网格是图的一个特例 · 网格与图同步走的动画 · 递归 DFS 在 50 万点上爆栈可学
- 31拓扑排序 换个入队条件的 BFS · 判环是白送的 · 答案不唯一时怎么对拍提高组 S可学
- 32最短路一:Dijkstra 朴素 → 堆优化 · 取最近的那个就定死了 · 上一章的堆原样搬过来 · 负权边到底断在证明的哪一步提高组 S可学
- 33最短路二:Floyd、Bellman-Ford、SPFA 与负环 k 为什么必须在最外层 · n−1 轮从哪来 · SPFA 就是上一章那份堆优化提高组 S可学
- 34最小生成树:Kruskal 与 Prim 切割性质 · 两个算法是同一条性质的两个推论 · 证明里没用到的条件,放开也不会有反例提高组 S可学
阶段 7 · 数据结构
- 35单调栈与单调队列 均摊分析:两层循环也能是 O(n) · 柱子和栈同屏的动画 · 一个对拍永远抓不到的 bug提高组 S可学
- 36并查集 路径压缩 / 按秩合并的复杂度与实测 · 三条曲线 · 五个「对拍一辈子抓不到」的写法可学
- 37堆与 priority_queue 这次的 log 证得死死的(对照上一章那个证不了的 α)· 建堆只要 O(n) · 上界要专门造数据才顶得到可学
- 38树状数组 lowbit 决定「攒多长」· 可以修改的前缀和 · 一个旋钮的两头正好是两个 bug 的天堂和坟墓提高组 S可学
- 39线段树入门 懒标记 = 欠着的修改 · 「必须下推」只是一个理由,不是定律 · 我预判的那个新旋钮,实测是负分提高组 S可学
阶段 8 · 数学
- 40GCD、LCM 与欧几里得算法 一行证明证的是「集合相等」· 最坏输入是斐波那契 · 随机数据会把这道题自己做没了可学
- 41质数:试除 → 埃氏筛 → 线性筛 那句 break 保证「只被最小质因子划一次」· 划的次数 = 合数个数 · ⚠ 次数少不等于跑得快可学
- 42快速幂与取模 把 b 看成二进制 · 次数只和 b 有关 · ⚠ 算法五行,坑有三处:溢出、负数、p = 1可学
- 43组合数与递推 取模之下不能除,只能乘逆元 · 把 40/41/42 三章收口 · 顺带还第 17 章那笔账提高组 S可学
阶段 9 · 补课
- 44高精度:加减乘,以及「为什么不能用 long long」 进位和借位是一条链 · 前导零的两头 · ★ 三个错法随机数据撞不到:结果为 0、位数变短、超出 64 位可学
- 45复杂度估算与考场策略 ★ 把「1 秒 ≈ 10⁸ 次」自己量出来:五档差 80 倍 · n 多大该往哪个复杂度想 · CE/WA/TLE/MLE/RE 各配一份真能跑出来的代码可学
- 46位运算:整数就是一排开关,以及 lowbit 为什么成立 异或的三条性质 · 七个动作共用一个套路 · ★ 还两笔账:lowbit 用了 113 次没讲过为什么,「位运算更快」实测下来快的那两行算的不是同一件事可学
阶段 10 · 字符串
- 47字符串基础:读进来、切开、比对 getline 会读到空行 · ★ size() 是无符号的,空串上 size()-1 是天文数字 · 「子串」不是「整词」可学
- 48KMP:失配的时候,i 一步都不用退 border 与 next 数组 · 算 next 本身就是「p 和自己匹配」 · ★ 朴素匹配在随机数据上一点都不慢,最坏那一档才差 496 倍提高组 S可学
- 49字符串哈希:把子串变成一个数,以及它会在哪儿撞 取子串就是第 6 章那一减 · ★ 本书第一个会给出错误答案的算法 · 自然溢出被 Thue-Morse 序列对任何奇数 base 卡死,单模数被生日攻击 3 万多次就撞出一对提高组 S可学
- 50Trie(字典树):一堆字符串摆成一棵树 查前缀只看串长,和串的数量无关 · ★ 路过计数和结尾计数是两个数 · 26 × 节点数 × 4 字节:同样的总长,内存能差三个数量级 · 末尾用 01-Trie 求最大异或对提高组 S可学
阶段 11 · 树上进阶
- 51LCA 与倍增:把「往上跳多少步」拆成二进制 ★ 和第 42 章快速幂是同一件事,只换了主语 · 查询是两段完全不同的循环 · ⚠ 建表必须用 BFS,链上递归必爆栈 · ★★ LOG 开小了:对拍看不见,随机大数据也看不见,只有又大又深的数据才现形提高组 S可学
- 52树上差分:把「整条路径 +1」变成「四个格子 ±1」 ★ 第 6 章那对逆运算换把尺子:前缀和 → 子树和 · 点差分四个标记 / 边差分三个 · ⚠ 上一章的 fa[root] = root 在这儿会让根被减两次 · ★★ 顺手生成器的两种漏法,两个精确的 0提高组 S可学
- 53树链剖分:把树拆成链,让「路径」变成「区间」 ★ 关键一步是换编号顺序,不是新结构 · 子树成区间是白送的,路径成 O(log n) 段只有「重儿子优先」给得了 · ★★★ 重儿子挑错答案全对、1800 轮对拍抓不到,只有数段数才看得见(梳子上 50000 vs 3)· ⚠ 「从一端生根的链」让三个错法同时失效提高组 S可学
节奏建议
每章 90~155 分钟,53 章平均 116 分钟 —— 这个数是把每章 frontmatter 里写的建议用时加起来算的(合计约 102 小时), 读讲解 + 自己敲一遍 + 做配套练习和对拍,三样都算在里头。
⚠ 它不是「一次坐下来两小时」:每章都带动画和对拍,拆成两三次更现实 —— 越往后的章节越长(第 39 章 140 分钟),别拿前面几章的手感去估后面。 按每周 2 章算,53 章大约 5 个月走完。
| 时间 | 阶段 | 阶段性目标 |
|---|---|---|
| 第 1–2 周 | 阶段 0 · 递归思维 | 能独立写出全排列和 N 皇后,不看任何参考 |
| 第 3–5 周 | 阶段 1 · 基础技巧 | 二分的边界能一次写对,不再靠试 |
| 第 6–7 周 | 阶段 2 · 排序与分治 | 手写归并排序并用它求逆序对 |
| 第 8–10 周 | 阶段 3 · 搜索 | 拿到一道搜索题,能立刻判断该用 DFS 还是 BFS |
| 第 11 周 | 阶段 4 · 贪心 | 会用对拍打假一个看起来很对的贪心 |
| 第 12–15 周 | 阶段 5 · 动态规划 | 背包的三种写法都能默写出来 |
| 第 16–18 周 | 阶段 6 · 图论 | 堆优化 Dijkstra 能默写 |
| 第 19–21 周 | 阶段 7 · 数据结构 | 并查集和树状数组成为肌肉记忆 |
| 第 22 周 | 阶段 8 · 数学 | 线性筛、快速幂当模板背熟 |
进度落后不要紧,跳步才要紧。宁可一章学两周,也不要为了赶进度把阶段 0 囫囵吞过去。