这是什么: 一套限时做题的流程 + 一张把「我不会」拆成「我卡在第几环」的复盘表。
为什么是现在: S1–S7
的零件你们已经读完了,S8
的题也动过手。知识缺口补上了,剩下的缺口是「在计时压力下把零件组装起来」。
从现在到 10 月,练的就是这个。
和 S10 的分工,先说清楚:
| 给你看什么 | |
|---|---|
S10 标程对照册 |
想通之后的样子 —— 干净的代码、笔直的推导 |
S9(本文) |
还没想通时那 25 分钟里在干什么 —— 包括试错和走过的弯路 |
⚠️ 你们说「算法思路想不通」,缺的是后者。 标程和题解都只展示结果,看一百份也学不到「怎么走到那里」,因为那个过程被删掉了。这一份把它补回来。
拿 小苹果(P9748,CSP-J 2023 T1) 做一次实录。这题
L01
例题二讲过,你已经知道答案了——所以正好可以专心看过程。
桌上 n 个苹果排一排(1 ≤ n ≤ 10⁹)。每天从第 1 个开始每隔 2 个取走 1 个(取走当天的第 1、4、7、… 个),剩下的重新排好,第二天继续。求:(1) 取完要多少天;(2) 最初的第 n 个苹果在第几天被取走。样例
8→5 5。
不是「读一遍看懂」,是拿笔圈:
1 ≤ n ≤ 10⁹ ——
这是全题最重要的一个数📝 读题读出「有几个问」这件事,每年都有人栽。
本题输出是两个数,样例 5 5
恰好相同,看漏一个问也能对上样例。
n ≤ 10⁹ ⇒ 查 S0 的复杂度表 ⇒ O(√n)、O(log n) 或
O(1)。
这句话立刻砍掉一大片:
a[n] 数组标记每个苹果 —— 10⁹ 个
int 是 4GB,开不出来⚠️ 这一步只花 2 分钟,但它决定了后面 20 分钟往哪走。 跳过它直接开写,八成写出一个正确但超时的程序,然后浪费 20 分钟调一个根本不该存在的 bug。
翻 S0 第三节:
冲突不是坏事,是信息。 它告诉你:这题是模拟,但需要降维。这就把搜索范围缩到很小了。
📝 决策表的正确用法不是"查到答案",是"缩小范围 + 发现冲突"。 冲突指出的地方,就是这题的难点所在。
这是最容易被跳过、也最值钱的 5 分钟。n = 8,拿纸列:
| 天 | 当天苹果数 m | 取走几个 | 剩下 |
|---|---|---|---|
| 1 | 8 | 3(位置 1、4、7) | 5 |
| 2 | 5 | 2(位置 1、4) | 3 |
| 3 | 3 | 1(位置 1) | 2 |
| 4 | 2 | 1(位置 1) | 1 |
| 5 | 1 | 1(位置 1) | 0 |
列到第 3 行,第一个规律自己就跳出来了:取走的个数是 m 除以 3 向上取整。8→3、5→2、3→1、2→1、1→1,全对。
第二问怎么办? 盯着表看第二遍,问一句:「最初的第 8 个苹果,什么时候轮到它?」
它排在最后。取苹果只取它前面的,所以它永远是最后一名。那它什么时候被取走?当位置 1、4、7、… 这串数正好数到 m 的时候,也就是 m % 3 == 1。
回表核对:第 5 天 m=1,1 % 3 == 1 ✓,答案 5 ✓。再往回看,第 2 天 m=5,5%3=2 ✗;第 4 天 m=2,2%3=2 ✗。只有第 5 天命中。
⚠️ 注意这里发生了什么:第二个规律不是"想"出来的,是"盯着已经列好的表问问题"问出来的。 空着手想,很难想到。
方案:不模拟每个苹果,模拟"还剩几个"。 每天
m = m - (m+2)/3,m 大约缩到 2/3。
10⁹ 缩到 0 要几天?大约 log(10⁹) / log(1.5) ≈ 51 天(实际跑出来是 50 天)。五十来次循环,随便过。
📝 写之前一定要把这个数算出来。 算完你才敢下手;算不出来就说明方案还没定好,这时候写代码等于赌博。
心里有表、有公式,写起来就是翻译。先把两个答案的变量名想好(days、lastDay),再写循环。
⚠️ lastDay 只能记第一次命中。 加一个
lastDay == 0
的判断——这种小地方,写的时候顺手加上,比调试时回来找便宜十倍。
别急着交。 三个必测:
| 测什么 | 为什么 |
|---|---|
样例 8 |
最低要求 |
n = 1 |
最小情况。1 天取完,1 % 3 == 1 所以 lastDay = 1 →
1 1 |
n = 10⁹ |
最大情况。主要看跑得快不快、有没有死循环 |
📝 n = 1
和最大值这两个,是所有题的通用自测。 花 90 秒,能挡掉一大半
WA。
| 时段 | 干什么 | 产出 |
|---|---|---|
| 0–3 | 读题,圈出每个数字和限定词 | 知道有几个问 |
| 3–5 | 数据范围倒推复杂度 | 砍掉一半做法 |
| 5–8 | 对 S0 决策表 |
缩小范围,找出冲突点 |
| 8–13 | 手玩样例,列表格 | 规律 |
| 13–15 | 定方案 + 算复杂度 | 敢不敢下手 |
| 15–22 | 写 | 代码 |
| 22–25 | 自测 n=1 / 最大值 | 敢不敢交 |
⚠️ 注意前 15 分钟一行代码都没写。 这不是磨蹭——T1 真正的写码时间只有 7 分钟,前面想清楚了,后面才快。 你们反馈「卡在题目和调试」,多半是因为前 15 分钟被压缩成了 3 分钟。
从 S8
的推荐做题顺序表里挑两道你还没交过的:
如果你们俩都做到第 5 题了,今天就是:分糖果 P7909(T1)+ 座位 P14358(T2)。
S8 的提示,一层都不看S10 标程对照册S1–S7(那是你的"考场笔记",查语法不算作弊)⚠️ 想不出来也不许提前结束。
剩的时间用来写暴力——S0 说过,暴力是分。能拿 40
分的暴力,价值远高于 0 分的空白。
1. 建文件夹,名字 = 题目英文名(如 candy)
2. 文件夹里建 candy.cpp
3. 代码里写 freopen("candy.in", "r", stdin);
freopen("candy.out", "w", stdout);
4. 同目录建 candy.in,粘样例输入
5. 编译运行,打开 candy.out 比对
⚠️ 交到洛谷时要把两行 freopen
注释掉(洛谷用标准输入输出)。交完立刻改回来——S7
爆零清单第 2 条就是这个。
📝 本地练习的文件名自己定即可,真正考场的文件名以当年题面为准。 别背我这里写的名字。
每道题一张。 抄进你的卡点记录本,或者直接打印这一页。
| 记录项 | 第一道 | 第二道 |
|---|---|---|
| 题号 | ||
| 开表时刻 | ||
| 读完题的时刻 | ||
| 想通思路的时刻 | ||
| 写完最后一行的时刻 | ||
| 第一次提交的时刻 | ||
| AC 的时刻(没 AC 写「未」) | ||
| 第一次提交结果(AC/WA/TLE/RE/CE) | ||
| 最后得分 |
四段耗时算一下(用上面的时刻相减):
| 阶段 | 用时 | T1 的健康值 |
|---|---|---|
| 读题 | ≤ 3 分钟 | |
| 想思路 | ≤ 12 分钟 | |
| 写代码 | ≤ 10 分钟 | |
| 调试 | ≤ 5 分钟 |
📝 哪一段爆表,问题就在哪一段。 这四个数字比「这题我不会」精确一百倍。
S7 查)第一次提交没过的话,勾一个:
做完两道题后填。 不会做的那道,从上往下问,在第一个答不上来的地方停住——那就是你的断点。
合上题面,用一句话说出「已知什么,求什么」。数据范围是多少?
把题目里的东西换成数学对象。这题的「苹果」「优惠票」「扑克牌」,本质上是什么的什么?
S8
每题的提示 1 就是在做这一步,把 12 道题的提示 1
连起来读,看它们都在干同一件事。数据范围允许你写几层循环?
S0决策表里,这题的关键词对应哪一行?
S0
第三节两张表背下来。这是全包最重要的一页,不是用来查的,是用来记的。思路你能讲清楚,但代码写不出来 / 写出来一团乱?
S10
标程对照册。这一环是唯一该看标程的时候。代码写出来了,样例过不了,然后你就停在那里了?
S7
第二节那四步,按顺序做,不许跳步。尤其第 1
步「手玩最小样例」,九成的人跳过它。📝 五环里,第 2 环和第 3 环是你们俩最可能的断点(因为你们说的是"想不通"而不是"写不出")。但必须自己走一遍问卷确认——猜错了就是吃错药。
按顺序,一步都别跳:
⚠️ 第 4 步的顺序不能反。 先看标程再复盘,你会以为自己"本来也想得到"——那是错觉,而且它会让你一直卡在同一个环节上。
📝 下次课的答疑窗口,带着你的复盘表和断点结论来。 不带就没得聊——20 分钟太贵,不能花在"你先说说这题讲什么"上。