打完比赛那几天,总会有一种很奇怪的状态:人已经离开屏幕了,脑子里却还在反复回放某道题的提交记录,尤其是那道卡了你四十分钟最后还是没过的大题。Codeforces Round 1089 (Div. 2) 结束后的当晚,我花了不少时间把整场比赛重新过了一遍,越复盘越觉得,很多人打 Div. 2 其实不是在跟题目较劲,而是在跟自己的节奏、判断力和考场习惯较劲。这篇回顾,我想从一场普通 Div. 2 参赛者的视角,把我认为最值得沉淀的东西拆开讲清楚——包括赛前怎么定目标、赛中怎么控时间、赛后怎么把一场比赛的价值榨干,以及从热搜里反复出现的那些题型里,我们能总结出什么样的思考框架。
不管你是刚接触 Codeforces 没多久的新人,还是已经卡在某个分数段想突破的老手,这篇内容都适用。比赛回顾这件事,做得好的人能比多做三场比赛收获还大,但前提是你得有一套自己的复盘方法,而不是对着提交记录干瞪眼。
1. 赛前的状态管理:比起刷题量,我更在意“可复现的答题状态”
很多人觉得 Div. 2 就是拼手速和知识储备,赛前多刷两道模板题、多背几个板子就够了。但我自己的体感是,Div. 2 的题目难度跨度其实非常大——A 题经常是签到题,B 题开始需要一点思维转弯,C 题和 D 题往往就进入区分度区间了。如果你赛前没有给自己设定清晰的分层目标,比赛中最容易出现的状况就是:A 题写得飞快,B 题卡住之后心态崩塌,后面 C 题和 D 题即使会做也来不及了。
1.1 赛前目标设定:先分清“保底题”和“冲刺题”
我每次打 Div. 2 之前,都会在备忘录里写一句话,比如“保底 AC 四题,冲刺五题,D 题如果 40 分钟没思路就放弃”。这个目标不是随便拍的,而是根据自己近十场比赛的通过情况算出来的。如果你最近十场平均只能稳定通过前两题,那第三题就应该当成冲刺目标来对待,而不是默认自己一定能做出来。
这里有一个很关键的认知:Div. 2 的分数分布不是线性的。 A 题和 B 题的分数差距可能只有 250 分左右,但 B 题到 C 题的难度跳跃往往是断崖式的,C 题到 D 题更是另一种维度。用跑步来类比,A 题是热身跑,B 题是匀速跑,C 题是间歇冲刺,D 题则是越野跑——你的体能分配方式必须完全不一样。
所以赛前真正要做的不是刷题,而是确认三件事:
- 自己最近的状态大概能稳定做出哪一档的题目
- 这场比赛的策略重点是“稳过 B 题”还是“死磕 C 题”
- 如果前 30 分钟连 B 题都没做出来,该在什么时间点启动止损机制
这三件事想清楚之后,哪怕比赛过程中出现意外,你至少有一个可以回归的“基准线”,而不是随波逐流地被题目牵着走。
1.2 比赛环境的“降噪”处理:把变量控制到最少
现场比赛或在家打 Virtual 比赛,最大的区别其实是环境变量。在家打 CF 的时候,很多人习惯开着聊天软件、时不时切出去看视频,这等于在给自己的反应速度叠加延迟。
我自己的习惯是开赛前十分钟做一次完整的环境检查:
- 把浏览器调整到无干扰模式,关掉所有无关标签页
- 确认模板代码、常用头文件、自带的调试输出宏已经准备好
- 简单测一下编译器和本地运行环境,确保不会出现“本地能过、交上去 CE”这种低级事故
- 准备一张草稿纸和一支笔——这听起来很土,但在推演构造题的时候比在编辑器里乱试高效得多
这些准备看起来琐碎,但它们都是在帮你把注意力完全集中在题目本身。比赛本身已经够紧张了,任何一环出问题都会放大焦虑感,所以我宁愿把赛前两分钟花在检查上,也不想赛中花二十分钟去排查环境问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 赛中答题节奏:从“看到题就写”到“先判断、再动笔”的转变
Div. 2 的赛中节奏,其实是整场比赛最让人琢磨不透的部分。有些人 A、B 两题加起来十分钟就过了,然后挂在 C 题上两小时;有些人 A 题写了四十分钟才过,但后面状态越打越好,反而过了四题。这说明时间分配不是越平均越好,而是要根据题目难度和自己的实时状态动态调整。
2.1 前 30 分钟的“快速试探”阶段
比赛开始后的前 30 分钟,是整场节奏的定盘星。这个阶段我通常不会在某一道题上恋战,而是先把四道题都扫一遍。这里说的“扫一遍”不是把题面读完就完事,而是要做三件事:
- 看数据范围,快速判断每道题大概率是什么复杂度(是 O(n log n) 还是 O(n²),是二分还是 DP)
- 看样例,尤其是样例的解释部分,有时候样例本身就在暗示构造思路
- 给每道题打一个“预期难度标记”,比如这道题我预期 20 分钟能 AC,那道题预期 40 分钟
扫过一遍之后,我会果断放弃“按题目顺序做题”的强迫症。如果 D 题的题意和数据范围让我瞬间有思路,而 C 题还完全没有头绪,那我会优先写 D 题。
这个选择需要解释一下为什么。Div. 2 的分数设计是越往后单题分值越高,所以同样花费 40 分钟,做出一道 D 题往往比做出一道 C 题拿到更多分数。而且从心理层面来说,在比赛早期啃下一道高分题,会给你后面所有题目带来强烈的正向反馈,这种“势能”很难量化,但非常真实。
2.2 卡题时的“止损线”和切题策略
卡题是每场 Div. 2 都会遇到的事,关键区别在于你什么时候意识到自己在卡题。我给自己定过一条规则:如果一道题在 20 分钟内没有任何实质性的思路推进,强制切换到另一道题,哪怕是回去检查 A 题的边界条件都比继续硬刚有价值。
这个规则背后的逻辑是:算法竞赛里的“灵感”往往是上下文切换触发的。当你长时间盯着同一道题,大脑的思维路径会固化,越来越难跳出自己给自己设置的框架。但当你去写另一道题,或者重新读一遍 C 题的题面时,大脑切换到新的问题域,反而有可能在潜意识里继续处理之前那道题,然后突然“蹦出”一个之前没想到的思路。
所以我通常的做法是:主攻一道题 20 到 30 分钟,如果思路走不通,就去做一道相对简单的题缓缓。等简单题写完、提交 AC,再回头来处理那道难题。你会发现,很多时候刚才那个没想通的关键点,在写另一道题的时候其实已经被某个数据结构或某个算法套路“间接启发”了。
2.3 样例通过不等于能 AC:提交前必须做的五件事
很多新人容易在样例通过之后立刻提交,然后收获一个 WA 再慢慢检查。但 WA 一次的代价不光是罚时,还会打断你的心流状态。我每次准备提交之前,哪怕在比赛中有时间压力,也会强制自己检查以下五项:
- 数据范围是否超过了 int,需不需要开 long long
- 数组是否开到题目给定的最大边界,有没有越界风险
- 边界条件(n=1、所有值相等、反向排序)是否覆盖
- 是否有未初始化的变量
- 输出格式是否和题目要求完全一致(比如换行、空格、大小写)
这五项检查大概只要花一两分钟,但能帮你避免大量的罚时。尤其是 Div. 2 这种节奏比较快的比赛,一次多余的 WA 可能就让你和上一个分数段差出几名甚至几十名。
3. 从热搜题型看 Div.2 常见的三类思维陷阱与应对思路
每次比赛结束,我习惯去看看社区里的讨论和热搜,不是为了看吐槽,而是为了看看大家集中讨论的题型。最近比较热的几个关键词里,“XOR of 3”和“Polycarp and Snakes”这两类题其实很有代表性,背后分别对应着位运算构造和模拟实现的经典考点。这两类题也是 Div. 2 里最容易让人“看着会做、动手就错”的题型。
3.1 位运算与 XOR 题:看起来是数学题,本质是状态压缩题
以 “XOR of 3” 这类题目为例,很多人第一次接触会觉得无从下手,因为 XOR 操作本身不像加减乘除那么直观。但如果你把它当成“按位操作”的语言来理解,这类题的核心思路就清晰了很多。
XOR 有一个很重要的性质:a ^ a = 0,a ^ 0 = a。 这意味着如果你想让一个序列满足某种条件,你往往可以通过两次操作消去某些影响,或者通过一次操作把某个状态转换为另一个状态。这种“反转”和“消去”的特性,让 XOR 题天然适合用来考查选手对状态变化的敏感度。
应对这类题目,我的常用分析框架分三步:
- 先把操作拆解成最原始的形式——这次操作到底改变了哪些位,能不能用公式表达出来
- 再考虑数据范围——如果 n 很大,大概率需要找规律或者数学推导,而不是真的去模拟
- 最后看样例输入输出,尝试从特殊解推出一般构造方法
很多时候,XOR 题的正解就是一个非常简洁的构造:比如通过几次固定操作把数组变成全零,或者把所有元素凑成某个特定的异或值。关键是你要敢去“猜”这个构造,然后用反证法或归纳法验证。
3.2 模拟与构造题:耐心读题比写代码更考验功力
“Polycarp and Snakes”这类题,看起来就是一个很直白的模拟题:给出一张图,然后判断是否能用若干条蛇的覆盖方式构造出来。但这类题真正的坑点往往不在模拟本身,而在题面的阅读理解。
这类题我见过太多人提交 WA,原因不是代码写错,而是题读漏了。比如蛇的覆盖方式、蛇是否必须连续、覆盖顺序是否有限制,这些细节如果不逐字逐句地抓,写出来的模拟代码跑样例能过,但一上大数据就挂。
对付这种题,我的建议是:第一次读题时不要急着写代码,而是把题面中的约束条件用自然语言重新表述一遍,必要时画成示意图。 这个过程大概花两三分钟,但能帮你把隐含条件全部显式化,避免后面反复读题、反复改代码。
另外,模拟题写代码时,一定要把“状态”和“动作”分离。比如这类题里的蛇,我会用一个对象或结构体来维护它的头部位置、长度、方向,然后每一步操作改成对这个状态的更新函数。这样即使思路中途需要调整,代码也容易改,不容易改一处崩三处。
3.3 两类题背后的共同思维陷阱:试图直接模拟,而不先做“可行性剪枝”
我发现“XOR of 3”和“Polycarp and Snakes”这两类题,新人最容易犯的共同错误是:拿到题之后立即开始写模拟代码,然后发现复杂度爆炸、边界条件处理不完,最后在泥潭里挣扎。
更好的思路是先做“可行性剪枝”——在动手写代码之前,先判断题目的条件是否可能成立。如果成立,再考虑构造;如果不成立,直接输出无解或特殊值。比如很多 XOR 构造题,你只要先算一下全部元素的异或和,就能快速排除掉一大半无解情况。而模拟题里,如果能先检查一下总数是否匹配、数量是否合法,也能省下大量无效计算。
这种“先做可行性判断,再做具体实现”的思维习惯,不仅在 Div. 2 里好用,在整个算法竞赛里都是通用的。你甚至可以把它当成一道题的“预处理步骤”——永远在写主逻辑之前,先把所有能快速判断的情况全部排除掉。
4. 赛后复盘的完整链路:如何把一场 Div. 2 的剩余价值榨干
比赛结束不等于学习的结束,恰恰相反,赛后复盘才是整场比赛学习效率最高的阶段。很多人都知道要复盘,但复盘的方法往往停留在“看题解、改代码、提交 AC”这三步,然后就扔到一边了。这种复盘方式其实是巨大的浪费,它把一场比赛里最有价值的“思维过程”部分完全丢弃了。
4.1 第一步:还原赛场上的真实思考轨迹
我赛后会做的第一件事,不是看题解,而是先把比赛时每道题的思考过程写下来。这个问题我当时是怎么想的?为什么思路会走偏?是在哪个地方卡住的?如果当时换一种思路会不会更快?这些信息如果不当场记下来,第二天就会忘得一干二净。
具体操作上,我会打开一个空白文档,按题目顺序记录:
- 每道题第一次看到时的直觉判断
- 实际做题时用了多长时间
- 中途尝试过哪些错误做法
- 最终卡住的点是什么
- 如果没有卡住,是通过什么关键观察豁然开朗的
这些记录不需要写得很正式,就是给自己看的流水账。但这份“思维轨迹”的价值在于,它能帮你识别出自己反复踩的坑。比如你会发现自己每次遇到区间类题目就喜欢下意识地用贪心,但这类题其实经常需要 DP——这种思维惯性问题,只有复盘时才看得最清楚。
4.2 第二步:按“错误类型”而不是“题目难度”分类整理
复盘的时候,我建议把错题按错误类型分类,而不是简单地按难度递进整理。常见的错误类型包括:
- 读题理解偏差:题目意思理解错了,导致写出来的代码解决的是另一个问题
- 算法选择失误:知道有哪些算法,但没判断对这道题适合哪种
- 边界条件遗漏:主逻辑写对了,但没处理某些特殊情况
- 思维定势阻碍:被自己熟悉的套路限制了,没想到更简洁的思路
- 代码实现错误:思路正确,但某个变量写错、某个循环条件写反了
把错题归类之后,你会发现自己的问题往往集中在某两类上。比如有些人读题理解偏差特别多,那说明他需要提升英语阅读精度或翻译能力;有些人边界条件始终处理不好,那说明他需要养成构造穷举测试数据的习惯。这种针对性训练比漫无目的地刷题高效得多。
4.3 第三步:写一版“可供复习的题解”,而不是“复制粘贴题解”
很多人看完题解之后,会直接把参考代码复制到自己的提交框里,AC 之后就算完事。但这种做法其实效率很低,因为你记住的是别人的代码,而不是别人的思路。
我自己的做法是:看完题解之后,把官方解题思路或排名靠前的 AC 代码合上,然后重新用自己的话把这题的解法写出来,再用自己的代码风格重新实现一遍。这个过程可能会比较艰难,甚至可能写到一半卡住,需要重新回去看题解。但正是这种“卡住—查找—解决”的循环,才能真正把题目的解法内化成你自己的能力。
这也解释了为什么我建议复盘时一定要记录“赛场上卡住的点”——因为那个卡住的地方,往往就是你思维的漏洞所在。题解告诉你如何绕过这个漏洞,但只有你自己重新思考一遍漏洞形成的原因,下次遇到类似情况时才不会再次掉进去。
4.4 补题之外:做一份“套路清单”和“思维导图”
复盘的最后一步,我会把这场比赛涉及的所有算法套路和思维模式整理成一张“套路清单”。比如这场比赛用了哪些常见的构造法?哪些题目可以通过二分答案来解?哪些题用到了贪心+优先队列的经典组合?
这份清单不需要很长,每场比赛能总结出三到五条就足够了。但经过几十场比赛下来,这些清单累积起来,就会变成你的“个人算法思维库”。参赛时遇到新题,我会先在脑海里飞快地过一遍这个思维库,看看有没有可以直接套用的思路。很多 Div. 2 的题目其实都是从经典题型变种过来的,当你积累了足够的套路清单,识别变种的能力会大幅提升。
5. 几场 Div. 2 打下来,我总结出的几条“铁律”与注意事项
在这一节里,我想分享几条打了很多场 Div. 2 之后,深深刻在我脑子里的经验教训。它们不见得能帮你直接 AC 某道题,但能帮你在比赛过程中少走很多弯路、少丢很多分。
5.1 永远不要高估自己对题意的理解速度
尤其是英文题面,很多术语和表达方式和中文的算法题描述并不完全对应。我见过不少人因为某个单词理解偏差,把一道简单的模拟题当成复杂的 DP 题做,白白浪费了二三十分钟。
我的建议是:遇到任何不常见的英文表达,先在草稿纸上用自己的话复述一遍题目的输入输出要求,确认无误后再开始想算法。这个习惯在 Div. 2 里尤其重要,因为速度虽然关键,但方向的准确度永远比速度的优先级更高。
5.2 压力下的 Debug:先定位问题,再动手改代码
比赛中的时间压力会让人产生一种冲动——看到 WA 就赶紧改一处,重新提交,期待能碰巧 AC。但绝大多数情况下,这种“盲改”只会增加罚时,不会解决问题。
正确做法是:先构造一个能复现问题的最小样例,在本地运行并打印关键变量,确认问题出在哪个环节,再动手修改。这个过程可能比盲改慢,但它的成功率几乎能到百分之九十以上,远高于盲改的碰运气。
5.3 不要过度迷信“别人都过了,我也一定能过”的心态
Div. 2 的题目通过率是公开的,你可以看到每道题有多少人提交、多少人通过。这个数据可以用来参考,但不要被它影响自己的判断。
有一次我打比赛,看到 B 题的通过率突然变高,觉得这道题一定很简单,于是把正常的思考过程压缩了,草草写了代码提交,结果 WA 了三次才过。后来复盘发现,这道题通过率高是因为大家都是在理解了某个关键性质之后才变得简单,而我正是因为“觉得简单”而没去深挖那个性质。
打比赛最怕的不是题目难,而是心态因为别人的表现而失衡。你要做的只是按照自己的节奏,把每一题都当成独立的挑战来对待。
6. 从赛场回归日常:把 Codeforces Round 的收获转化为长期能力
比赛会结束,分数会变化,但真正留下来的应该是能力和认知的提升。一场 Div. 2 的收获,不应只体现在排名上,更应体现在你对算法的敏感度、对题型的识别能力、对时间管理的掌控力上。这些能力只有在复盘和反复练习中才能沉淀下来。
我自己的习惯是,每场比赛结束后,会在接下来的一到两周里,把比赛里没有做出来的题目作为日常练习的重点。不是简单地把题解看了、代码粘贴一遍就完事,而是隔三五天再重新做一次,看自己是否还能独立 AC。如果第二次依然能做出来,说明这题的知识点已经真正内化了;如果做不出来,说明当初的“会了”只是幻觉,还得继续深入。
此外,我也会把每场比赛里的一些有价值的题目记录进自己的学习笔记里,附带上赛时思路和赛后总结。这些笔记不像题解那样条理清晰,更像是我和题目之间的“私人对话”。但正是这种对话,让我越来越了解自己的思维习惯和短板所在。
如果你想在 Div. 2 里稳定提升,我的核心建议其实很简单:把每一场比赛都当成一次完整的项目来对待——赛前准备、赛中执行、赛后复盘,三个环节一个都不能省。 很多人只重视赛中的两个小时,但真正拉开差距的,往往是你赛前怎么准备、赛后怎么总结。这场 Codeforces Round 1089 (Div. 2) 的回顾,如果只能留下一条经验,我会选择这一条:比赛真正的价值,不体现在当时的分数上,而体现在赛后你能从中学到什么。带着这种心态,你会发现每一场比赛都值得打,每一次回顾都有收获。
