周日晚上的八点,AtCoder Beginner Contest 431准时开打。这个系列从第一场走到今天,已经撑过了几百个周末,说它是算法竞赛圈里最稳定的赛程之一毫不夸张。我到现在还记得自己第一次参加ABC时,连快速输入模板都要现场翻博客的样子;而如今打开比赛页面,我想的不再是“这题我会不会”,而是“这场比赛我应该怎么打”。这个差别,恰恰是很多刚入坑的选手最容易忽略的东西:比赛不是几道题目的简单堆叠,而是一场需要策略、节奏和复盘的综合训练。这篇文章不打算把ABC431的每道题逐题贴出来,那是官方题解区大佬的工作;我想做的事情是,用一场老选手的视角把参赛全流程拆开揉碎:赛前做什么、赛中怎么分配时间、卡题了怎么办、赛后怎么补题,以及从ABC走向ARC、再走向ICPC的路线图。对刚开始刷ABC的朋友,它是一份可以直接抄作业的参赛清单;对已经卡在C、D题很久的朋友,它也许会告诉你,问题其实出在比赛之外的地方。
1. 一场持续一百分钟的自测:ABC431到底在比什么
1.1 Beginner这个名字,坑了多少人
ABC的全称是AtCoder Beginner Contest,直译就是“初学者竞赛”。我第一次看到这个名字的时候,以为它和那些编程入门练习题库差不多,做对几道简单题就能收工。真正上场之后才发现,这个名字的迷惑性相当大:A题和B题确实在照顾新手,但往后的题目会非常快地滑向真正的算法竞赛难度。C题开始出现需要推理的规律题,D题可能是动态规划或者图论的入门口,E题之后基本就是熟练选手和普通玩家之间的分水岭。
很多新人会陷入一个误区:既然叫Beginner,那我只做A、B题就够了,等以后强了再做后面的题。这个想法浪费了ABC最大的价值。ABC本质上是一场限时一百分钟的段位测试,它和期末考试一样,考的不是“你会不会做某道题”,而是“你在时间压力下能稳定拿到多少分”。只挑简单题做,等于每次考试只写选择题就交卷,永远不会知道自己真正的位置在哪里。
我的建议是把ABC当作一次真实的能力体检:A、B题决定你的下限,C、D题决定你的段位,E题往后的题目决定你还有多少成长空间。哪怕你目前只能AC前两题,也该在比赛结束后把所有题目都看一遍,至少搞清楚后面的题在考什么方向。
1.2 四百三十场沉淀下来的出题套路
ABC能成为很多算法竞赛选手的日常训练场,核心原因不是单场题目有多惊艳,而是这个系列足够稳定、足够连续。每周一场,几乎全年不休,数到场次编号来到431,说明这个平台背后有一套非常成熟的出题体系。
以我刷过这么多场的经验来看,ABC的题目分布虽然每一场都有变化,但整体风格存在明显的结构特征:前面的题目偏向基础语法、数学推导和简单模拟,中段题目开始考察算法建模能力,后面的题目往往需要结合多种技巧。为了让你更容易理解,我这里列一下常见ABC题目和高频考察方向的对应关系:
| 题目位置 | 常见考察方向 | 在ABC431中的可能形态 |
|---|---|---|
| A题 | 基础输入输出、四则运算、条件判断 | 一般是最直接的签到题,热身用 |
| B题 | 字符串处理、简单模拟、枚举 | 需要快速读懂规则,代码量不会大 |
| C题 | 数学推导、排序、二分答案、基础图论 | 常见的一道分水岭题 |
| D题 | 动态规划、贪心、数据结构入门 | 开始真正区分“会”和“熟” |
| E题 | 图论、组合数学、复杂DP | 常用到AtCoder Library里的现成算法 |
| F题及以后 | 进阶数据结构、高级图论技巧 | 冲击高段位的选手在这里拉开差距 |
这不是说ABC431一定严格遵守这个表格,但理解这个典型结构能帮你在赛场上建立预期。你不需要等到看完所有题面才行动,而是可以根据题目编号大致判断每道题的难度区间,然后更从容地安排时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开赛前的阵地检查:环境、模板和状态
2.1 用C++还是Python:选错语言真的会掉分
先聊一个每次比赛前都会被反复问起的问题:参赛到底用C++还是Python?我的看法是,如果你是刚开始打比赛,C++应该是长远来看的优先选择;如果你已经把Python用得得心应手,参加ABC的前几题完全没问题,但遇到后面的大数据量题目可能会比较难受。
原因在于两种语言在竞赛中的定位不同。C++的编译运行速度快,标准库里的STL容器操作方便,基本上所有算法模板都有成熟写法,所以在面对数据范围较大的题目时,C++的容错空间更大。Python的优势是语法简洁、开发速度快,非常适合用来验证思路、快速实现不太复杂的数据结构,但在同样时间限制下,Python有时候会因为常数太大被卡超时。
我见过不少选手在ABC里用Python一路打到水色,也见过有人因为习惯了Python的便利,后期转C++的时候痛苦了一个月。如果你目前还处于入门阶段,我的建议是两条腿走路:平时用Python刷简单题建立思路,同时有意识地开始看C++的常用模板,等到C、D题开始成为常态时,再逐渐把主力语言切换到C++。这个过渡在前期做,成本最低。
2.2 一份够用的竞赛模板清单
很多人一听到“准备模板”,就以为要把所有算法都背下来,这是另一个误区。模板的作用不是让你背天书,而是减少开赛后的基础代码时间。真正上赛场的时候,你的注意力应该花在读题、分析和调试上,而不是现场回忆快速输入怎么写。
以Python为例,我每次比赛前都会确保以下这段模板随手就能敲出来:
python复制import sys
input = sys.stdin.readline
def solve():
n = int(input())
a = list(map(int, input().split()))
# 主逻辑从这里开始
ans = 0
print(ans)
if __name__ == "__main__":
solve()
很多新手在本地写代码时习惯直接用input(),这在数据量小的时候没问题,但ABC后面几题的输入规模动辄十万、二十万,Python自带的input()会带来肉眼可见的延迟。改成sys.stdin.readline之后,速度提升立竿见影。
第二类建议备好的模板是并查集(DSU),因为它出场频率非常高,从判断连通性到最小生成树都会用到:
python复制class DSU:
def __init__(self, n):
self.parent = list(range(n))
self.size = [1] * n
def find(self, x):
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra == rb:
return False
if self.size[ra] < self.size[rb]:
ra, rb = rb, ra
self.parent[rb] = ra
self.size[ra] += self.size[rb]
return True
还有二分查找的模板,这东西看起来简单,但边界条件很容易写错:
python复制def lower_bound(arr, x):
lo, hi = -1, len(arr)
while hi - lo > 1:
mid = (lo + hi) // 2
if arr[mid] >= x:
hi = mid
else:
lo = mid
return hi
我个人不推荐背一大堆代码,但上面这几个都属于高频使用的“肌肉记忆型”模板,建议在赛前亲手敲几遍,不要在比赛时才去找。
2.3 开赛前五分钟的固定动作
很多人觉得比赛是八点整才开始,其实真正的比赛从你坐到电脑前的那一刻就已经开始了。我的固定流程是这样的:提前十分钟打开比赛页面,确认账号状态正常;打开代码编辑器,把快速输入模板和常用数据结构模板先写好放在一边;再浏览一下整个页面的题目列表,做到心里有个概览。
还有一件容易被忽略的事:检查自己后台有没有开太多无关网页。ABC的时间只有一百分钟,中途因为浏览器卡顿或者电脑弹窗浪费一分钟都非常可惜。赛前把消息软件静音,把电脑的自动更新关掉,这些看起来琐碎的事情,在高压比赛里都会变成影响成绩的变量。
心态上也值得提前调整。我给自己定的比赛目标是“把会做的题目稳稳做对”,而不是“每道题都要AC”。这个默认值能帮你避免很多比赛中的自我较劲。遇到不会的题是正常的,不是每道题都必须当场解决,有些题目就是用来让你发现自己短板的。
3. 比赛里的时间账:从A题到F题的节奏控制
3.1 ABC的难度曲线不是线性的
如果你只看题目编号,会觉得ABC的难度一定是一道一道递增上去的,但实际情况并不是一条平滑的曲线。有时候B题会比C题更绕,有时候D题反而是整场里最容易拿分的题,E题却突然难度跳变。
所以,不要被题目编号锁死。我的习惯是开赛后先把所有题目都扫一眼,尤其是把各题的样例读一遍。样例往往能透露出题人想让你用什么方式理解题目。如果一道题编号很靠前但题面长度异常,那多半有需要仔细理解的细节;如果一道题编号靠后但样例特别直观,也许它没有想象中那么难。
这种全卷浏览还有一个隐藏好处:它能帮你尽早判断本场的整体难度风格。当你看完所有题目后,心里会有一个大致的优先级:哪些题是送分题,哪些题需要战术后撤,哪些题值得全力一搏。这个优先级就是接下来一百分钟里你的行动地图。
3.2 时间分配参考表
以下是我个人比较常用的时间分配节奏,适合大多数普通ABC场次,但绝不是万能公式:
| 时间段 | 目标 | 备注 |
|---|---|---|
| 0-10分钟 | AC A题、B题 | 这两题通常不需要过于谨慎,但也不要因为轻敌看错条件 |
| 10-35分钟 | 主攻C题 | C题是一道典型的分水岭,30分钟后如果还没思路,果断标记跳过 |
| 35-70分钟 | 进攻D题和E题 | 这一阶段是整场比赛的黄金时间,也是最容易拉开差距的区间 |
| 70-90分钟 | 回头处理跳过的题 | 用剩余时间重新看C题或卡住的题目,新思路往往在这时候出现 |
| 最后10分钟 | 检查提交和边界条件 | 不要在这个时间写新算法,只做验证和修正 |
这个时间表不是让你掐着秒表做题,它更像是一个预警系统。当我在某道题上停留时间超过它的预算时,就会有意识地提醒自己:该不该继续投入?这个提醒非常重要,因为比赛中最贵的资源不是思维,而是时间。
3.3 跳题与止损:什么情况下必须放弃
“跳题”这个词在很多人看来是一个消极的选项,但恰恰相反,主动止损是一种高价值策略。我见过太多选手在C题上死磕了四十分钟,最后C题没做出来,后面明明有更简单的D题也没时间写,相当于整场比赛提前结束。
什么时候该跳?我的标准很简单:如果一道题你读完题面之后,在二十分钟内连一个可行的思路框架都搭不出来,就应该先放下它。放下不代表放弃,你可以换个角度再来,也可以去看后面的题。很多时候,当你从别的题目的思路里绕一圈回来,之前的思维定式会被打破,再看旧题目反而会有新灵感。
更重要的一点是,比赛中的止损决定要果断。不要反复犹豫“我是不是再想五分钟就能做出来了”,这种念头消耗的注意力远比五分钟多。先去做能拿分的题,把确定的分拿到手,再回来啃硬骨头,这才是理性决策。
4. 卡题时刻:真正卡住你的往往不是算法
4.1 三个典型的“假卡题”
卡题的时候,第一反应总会是“这个算法我不会”。但根据我的经历,比赛中真正导致长时间卡住的原因,绝大多数不是知识盲区,而是下面这三种“假卡题”。
第一种是题意理解偏差。题目里一句“最多”“至少”“连续”“不连续”没看清,整个建模方向就完全跑偏。比如你以为是求最大值,实际题目问的是方案数;你以为可以重复选择,实际上每个对象只能用一次。这类错误非常隐蔽,因为你看自己的代码会觉得逻辑自洽,只有对着样例反复推敲时才能发现偏差。
第二种是复杂度估算失误。一道题看起来用双重循环能过,你顺手写了一个O(n²)的解法,结果本地样例过了,一提交就超时。这种问题往往不是算法思维不够,而是对数据范围不够敏感。看到n=2乘10的五次方时,要立刻意识到O(n²)已经不可行,应该往O(n log n)或者O(n)的方向想。
第三种是边界条件漏判。空数组、最小输入、全部值相等、最大值刚好在类型上限附近,这些情况最容易让你的代码在某个不起眼的地方崩掉。竞赛题很多时候就是在考察你对边界情况有没有足够的敬畏心。
4.2 一次完整的卡题自救流程
遇到卡题时,我会用一套固定的流程来排查,而不是坐在那里干想。这套流程把我从无数次“卡住”里捞出来,所以分享给你。
第一步,重新读题。把题目从头到尾读一遍,重点看输入输出约束和关键条件,不要跳着看。很多时候你以为自己读过了,实际上大脑只是扫过了一些关键词,根本没有理解完整含义。
第二步,手算一遍样例。不要直接跑代码,先用笔在纸上模拟样例的每一步,把期望输出推导出来。如果手算结果和题目给的一致,说明你对题意的理解暂时正确;如果不一致,那问题大概率出在理解层面。
第三步,造小规模边界数据。尝试n=0、n=1、全部相同、随机生成小数据,这些情况最容易暴露问题。把程序跑出来的结果和自己手算的预期做对比,如果出现了不一致,就用调试器或者print语句逐步检查中间变量的变化。
第四步,如果还是找不到问题,回到暴力解法。很多卡住的题其实你已经有了一个较优的思路,只是某个细节没处理好。这时候用一个简单的暴力解法做参照,在随机小数据上与你的优化解法对拍,通常很快就能锁定差异在哪里。
这个流程最重要的价值是让“卡题”成为一个可操作的问题,而不是一种情绪。你不再焦虑“我怎么这么菜”,而是按部就班地排查题意、边界和中间状态,问题反而更容易水落石出。
4.3 比赛中的情绪管理
说到情绪管理,很多技术文章都不太愿意聊,但它和算法能力一样决定比赛成绩。我自己的体验是,比赛中最容易崩盘的时刻,不是遇到难题,而是连续提交失败后被一种“我今天肯定打得很差”的念头裹挟。这种情绪会让你的注意力从题目本身转移到自我评价上,接下来的每一步判断都会变形。
我的应对方式是给自己设一个“掉分预案”:开赛前就想好,哪怕这场比赛掉分,也不过是rating曲线上的一个小波动,真正重要的是我在这一百分钟里有没有学到新东西。把这个预案写在便利贴上,或者记在脑子里,等到情绪开始波动时,它就是一个让你回到理性状态的小锚点。
此外,比赛结束后不要立刻陷入自我否定。如果你发现自己在一道题上卡了很久,与其说“我怎么连这个都不会”,不如说“这一道题暴露了我某个方面的盲区”。同样的失败,转换成学习信号之后,复盘的效率会高出很多。
5. 赛后两小时,决定你与别人的差距
5.1 补题的正确打开方式
比赛结束并不意味着学习结束。我在自己刷题的早期阶段,打完一场ABC就看一眼排名然后关掉页面,第二天什么都想不起来,等于把一场很有价值的考试给浪费了。后来我才意识到,赛后两小时的补题,才是真正拉开选手差距的地方。
补题的第一原则是:不要急着看题解。哪怕你完全没做出来,也应该先给自己一个重新思考的窗口。看十分钟题目,尝试用自己的语言描述思路卡在哪里,具体是哪一个点想不通。这样做了再去看题解,你才能精准理解题解里最关键的思维跳跃发生在哪里,而不是囫囵吞枣地抄一遍代码。
看完题解之后,一定要关掉参考代码,自己重新写一遍。很多人觉得“看懂了”就等于“会了”,但真正动手写的时候,才会发现有多少细节其实没理解透彻。重新写的过程中遇到卡顿,就是你需要再去翻题解的地方,带着问题去看,记忆会牢固得多。
5.2 五步复盘法
我给自己定了一个简单的五步复盘流程,每场比赛结束之后都会走一遍,大概花半小时到一个小时。这套流程不复杂,但长期坚持下来效果非常明显。
第一步,还原赛场思路。打开提交记录,回忆每一道题当时的想法是什么,是真的会做,还是半猜半蒙写出来的。这一步帮你区分“运气分”和“实力分”。
第二步,标记失误点。把比赛中的关键失误按类型归类:是题意理解错误,还是边界条件漏判,或者是复杂度估算失误。归类越细,后面的改进就越有针对性。
第三步,统计时间损失。看看自己在哪些地方浪费了太多时间,有没有本该更早跳题的决策失误。时间记录能帮你发现自己的节奏问题。
第四步,补题重写。这是最重要的一步,把比赛里没AC的题,在没有参考代码的情况下重新写一遍,直到通过。
第五步,归类到自己的知识图谱。这个题目属于哪个主题,用到了哪些算法和思想,和你以前做过的哪些题有联系。把新题挂到旧知识上,它才能真正进入你的长期记忆。
5.3 用AtCoder Problems管理你的错题
补题的时候,我强烈建议配合AtCoder Problems这个工具来用。它把每一场ABC的所有题目都按难度、通过率、主题整理得很清楚,你能直接看到某道题在你这个段位的通过率是多少,也可以按算法主题筛选题目。
我的用法是赛后把本场没AC的题都加进自己的题目集合,并且标注好两个标签:一个是这道题的算法方向,比如“动态规划”“图论”“数学推导”,另一个是当时的失误原因,比如“边界条件没考虑”。这样积累一段时间后,再回头看,你会对自己在哪些主题上反复吃亏一目了然。
做错题本的意义在于避免在同一个坑里反复跌倒。算法竞赛的知识点那么多,不可能每次都靠“临时想起来”来覆盖,建立一个自己能检索的知识索引,会让补题的收益成倍增加。
6. 从ABC到ARC,再到ICPC:进阶路线怎么走
6.1 ARC076为什么值得翻出来做
当ABC的题开始不能满足你的时候,很多人会把目光转向更高一级的ARC,也就是AtCoder Regular Contest。ARC的定位比ABC更难一些,题目更加看重思维深度和算法设计能力。如果让我推荐一场值得补的ARC,ARC076是一个很好的选择。
ARC076里面有一道非常经典的D题,题名叫Built?,大意是有若干点,两点之间的连边代价是横坐标之差的绝对值和纵坐标之差的绝对值中的较小值,要求构造最小生成树。题面看起来很简单,但直接对所有点两两建边会得到平方级别的边数,完全无法处理大规模数据。经典解法是先按横坐标排序,只把相邻点之间的边加入候选;再按纵坐标排序,同样把相邻点之间的边加入候选;最后在这个缩减后的边集上跑一遍Kruskal。
这道题的精髓在于“减少搜索空间”:不是所有点对都值得作为候选边,而是通过排序把真正可能进入最小生成树的边筛选出来。这种思想会反复出现在更高级别的竞赛题目里,从ABC的D题到ICPC的题都可能用到。翻出ARC076来补,不只为了这一道题,更是为了理解这类“先压缩再求解”的思维方式。
6.2 ICPC线上选拔赛的真实体验
如果你在ABC和ARC里已经积累了一定的题量,有朝一日可能会想尝试团队赛。ICPC就是算法竞赛领域最知名的团队赛事之一,三人一队,共用一台电脑,在五个小时左右的时间里解决尽量多的题目。2023赛季的亚洲东大陆区网络选拔赛,像这类线上赛就是进入区域赛前的一道重要关卡。
ICPC线上赛和ABC最大的不同,从个人赛变成团队协作。ABC里你只需要管好自己的节奏,但ICPC里三个人需要同时处理不同难度的题目,还需要有人负责读题、有人负责写代码、有人负责验证思路。这种分工如果没有提前训练,赛场上很容易出现三个人挤在同一道题上浪费时间的场面。
所以我的建议是:先利用ABC把个人的算法基本功打扎实,再去参加ICPC类型的团队赛。个人能力是团队协作的地基,如果每个人都能稳定解决自己负责难度区间内的题目,团队的整体发挥会非常稳定。
6.3 给不同目标选手的三条训练路径
到了最后一节,我想把进阶路线说得更实在一点。根据你的目标不同,训练方式其实可以有明显的侧重。这里我用一个表格总结三条路径,你可以对照自己的情况来选:
| 目标定位 | 参考训练内容 | 每周建议 |
|---|---|---|
| 享受比赛、巩固基础 | 稳定参加ABC,补完C题和D题 | 1场ABC + 2-3道精选题 |
| 冲击绿色、水色段位 | ABC全补 + 适当刷ARC前几题 | 1场ABC + 1场ARC + 主题专项训练 |
| 备战ICPC等团队赛事 | 在ABC/ARC基础上增加团队训练、专题套题 | ABC保持手感 + 团队模拟赛 |
无论选择哪条路径,一个共同的建议是保持可持续性。每周打一场ABC确实不难,难的是赛后还能坚持花时间复盘和补题。很多人一开始热情高涨,连续两周每天刷题,第三周就彻底断掉。与其这样,不如给自己定一个更温和但能长期维持的节奏,哪怕每周只认真做透两三道题,也比心血来潮狂刷三天有效得多。
我打了这么多场ABC,最大的体会是:这个系列真正锻炼人的地方,从来不只是“会做某一道题”,而是让你在一次次限时压力中学会决策、学会止损、学会把失败转化为可执行的复盘动作。ABC431也只是这条长路中的一站而已。下一次比赛开始时,你不妨先把这场复盘的收获带上去——开赛前检查好环境,赛时控制住节奏,赛后踏踏实实补题,剩下的就交给时间。
