ACC-C组初赛听起来就是一个很“实在”的竞赛——没有花哨的框架没有复杂的前后端联动就是纯粹地用C去解决一个又一个算法问题。我参加过几届类似性质的校内算法竞赛也带过一些学弟学妹备赛很理解那种“题目看着都会一提交就WA”的挫败感。这篇文章就结合我自己的参赛和辅导经验从赛制分析、备赛方法、题型套路、考场策略到赛后复盘把C组初赛从里到外拆一遍。不管你是第一次参加算法竞赛的新手还是已经有一定刷题量但总在比赛中发挥失常的老手这篇内容应该都能给你一些参考。1. 初赛到底在考什么赛制、环境与评分逻辑1.1 机试赛制与晋级规则ACC的C组初赛通常采用机试形式在统一的时间段内登录在线评测系统OJ完成若干道编程题。常见的题量在6到8道之间比赛时长一般在3小时左右部分场次会缩短到2小时。这里有个很容易被忽略的点初赛的晋级依据往往不是“做出几道题”这么简单而是综合了解题数量、罚时和题目分值。罚时这个概念很多第一次参加比赛的人会掉坑。它并不是指你提交错了会被扣多少分而是指“这道题从比赛开始到你最终通过所用的时间”再加上“错误提交次数乘以一定的惩罚时间”通常是20分钟。举个例子你花了40分钟过了一道题期间错了3次那这道题的罚时就是40 3×20 100分钟。最后排名时解题数相同的人罚时少的排前面。所以初赛的隐藏考点之一就是减少无谓的错误提交。我见过太多水平不错的选手因为习惯“先交一发试试”而输给了罚时。养成“本地充分验证后再提交”的习惯在初赛阶段比多刷几十道题还管用。1.2 题目难度梯度与分值分布初赛题目的设计通常有一个明显的梯度我用一张表来总结常见的结构题号位次预估难度典型考察点目标策略A/B两题签到题输入输出、简单模拟、基础数学必须全对且要快C/D两题简单算法排序、贪心、二分、简单DP必须拿下这是晋级基本盘E/F两题中等题图论基础、复杂模拟、区间问题力争突破拉开差距G/H两题压轴题数据结构优化、状态压缩、数学推导量力而行拿部分分很多人一上来就按顺序从A题做到H题这是个误区。正确的做法是先花两三分钟把全部题目都扫一遍尤其是看看每道题的数据范围——数据范围往往暗示了期望的时间复杂度。比如n ≤ 10^5那基本告别O(n²)的算法n ≤ 20那八成是状态压缩n ≤ 10^18那很可能是数学题或者矩阵快速幂。1.3 赛前环境适配比想象中更重要初赛一般在指定的OJ上进行但各个赛点的编译器版本、环境配置并不完全一致。我建议赛前至少做三件事第一确认评测机的C标准。有的OJ默认是C17有的还停留在C11。如果你习惯用std::gcdC17才提供或者结构化绑定C17特性而评测机是C11编译这关就过不去。第二用比赛OJ的在线IDE试跑一次“输入输出模板”。很多新手挂在第一道签到题上不是不会做而是不熟悉评测系统的输入输出方式。搞清楚是从标准输入读数据、标准输出写结果而不是读写文件有些比赛要求读写文件那文件名和路径就绝对不能错。第三提前配置本地环境。初赛阶段我用的是本地编辑器加命令行编译的方式比拼大型IDE更轻量也更接近比赛环境。下面这组命令是我常年使用的g -stdc17 -O2 -Wall -Wextra main.cpp -o main ./main input.txt output.txt-O2是为了和评测机的优化等级对齐-Wall -Wextra能帮你抓出一堆潜在的未定义行为初赛阶段开这两个编译选项非常值。2. 备赛阶段的侧重点从“会刷题”到“会比赛”2.1 初赛核心算法清单初赛考察范围虽然比复赛窄但也不是完全无边界。结合近几年各大高校算法竞赛初赛的出题规律我整理了一份“必须稳拿”的知识点清单基础数组、字符串、结构体排序、前缀和与差分算法二分答案、双指针、贪心、递推与简单线性DP数据结构栈、队列、优先队列堆、并查集、单调队列/单调栈图论链式前向星或vector存图、最短路Dijkstra、拓扑排序、最小生成树数学最大公约数、快速幂、质数筛法、组合数基础、取模运算在这些里面最容易在初赛翻车的是前缀和与差分。很多选手直到复赛才知道差分这个工具的存在实际上初赛的区间修改题恰恰是差分的高频考点。举个典型例子给你一个长度为n的数组做q次操作每次把[l, r]区间整体加上x最后输出整个数组。朴素做法是每次循环区间赋值复杂度O(nq)而差分数组可以做到O(n q)——这类题就是初赛的“分水岭题”。2.2 刻意练习把解题变成条件反射备赛阶段最忌讳的就是“用刷题数量感动自己”。我看过不少同学在OJ上刷了三四百题但比赛时依然发挥不稳定原因在于他们练的是“看了题解写代码”而不是“独立解题”。我建议把刷题模式调整为“模拟赛训练”。每次挑4道题限定90分钟用白纸打草稿推演思路写出完整的代码然后用对拍程序验证。对拍是算法竞赛里非常实用的技巧写一个暴力解法保证正确但可能慢再写一个优化解法可能是你要提交的版本然后用随机数据生成器同时喂给两个程序比对输出是否一致。我习惯用Python写数据生成器然后用bash脚本跑对拍for i in $(seq 1 10000); do python3 gen.py input.txt ./brute input.txt ans1.txt ./fast input.txt ans2.txt if ! diff -q ans1.txt ans2.txt /dev/null; then echo 找到差异第 $i 组数据 break fi done如果你的优化解法在1万组随机数据上都和暴力解法输出一致那正确性就有相当保障了。对拍能帮你发现那些“样例过了但思路有瑕疵”的隐藏Bug是我个人最推荐的备赛利器。2.3 补题比刷题更重要每次模拟赛或者正式比赛结束之后不管成绩好坏我会立刻把没做出来的题目标记出来当天晚上就补完。补题不是看一遍题解再照着敲一遍而是自己先独立思考至少半小时明确卡在哪个环节再去看题解重点看别人的思路和自己的差异在哪里合上题解重新独立实现一遍隔两天再回来做一遍确认真的掌握了。这个过程坚持两到三周效果会比盲目刷题好得多。初赛阶段比的不是谁会解的题型多而是谁能在有限时间内稳定地把会做的题做出来补题就是在强化这种“迎战陌生题目”的稳定性。3. 初赛题目实战拆解四类高频题型的解题套路3.1 模拟题读题与状态设计是生死线初赛必定有至少一道模拟题而且往往不是单纯的“照着规则翻译成代码”。模拟题的难点通常不是算法而是状态设计的清晰度。举一个我辅导过的学弟遇到的典型题目有一组机器人每个机器人有位置和朝向每秒移动一格碰到边界会掉头问t秒后所有机器人的位置。如果脑子里没有“把掉头映射为速度取反”的抽象就会陷入“每秒判断一次是否碰边界”的繁琐逻辑还容易漏掉两个机器人重叠时的情况。这类题的正确打开方式是先用数学建模简化问题。比如“碰到边界掉头”其实等价于“沿着一个长度为2L的无限循环移动”这样就可以用取模运算直接计算t秒后的位置而不需要逐秒模拟。所以做模拟题时我给自己定的规矩是动手写代码前先在注释里把自己的“状态表示”写清楚每个变量代表什么、循环不变式是什么都写出来再动笔。3.2 贪心与排序优先队列是常客贪心题在初赛里的出场率非常高而且很多都伴有“选择最值”的意味。比如经典的“会议室安排”问题给定n个活动的开始和结束时间问最多能安排多少个活动。标准解法是按结束时间排序然后依次选择结束时间最早且不冲突的活动。但初赛的贪心题往往不会这么直白它们经常和优先队列结合。我记得有一道题是这样的你有n个任务每个任务有截止时间和完成所需时间问最多能完成多少个任务。这道题的正确解法是“按截止时间排序用小顶堆维护已选任务的耗时如果当前任务放不进去就弹出耗时最大的那个”本质上是把“放弃哪个任务”的决策交给了堆。这类思路如果之前没见过赛场上现场想出来会消耗大量时间。所以我建议备赛时对“贪心 堆”的组合保持高度敏感这几乎是中等难度题的标准配置。3.3 二分答案重点在判定函数的单调性二分答案在初赛里属于“看着不会、点破简单”的题型。它的难点不在于二分本身那只是十几行代码而在于如何设计一个check函数。我遇到过一个很典型的题目把n件物品分成k组每组的重量不能超过w问w最小是多少。如果直接想贪心分组会很纠结但如果换个角度给定一个w判断能不能分成k组这就变成了一个简单的线性扫描问题。判定函数的逻辑是“按顺序尽量多地往当前组里塞物品塞不下就开新组”最后数一数组数是否超过k。有了这个判定函数之后答案w一定在某个区间内单调于是二分即可。二分边界问题是我见过大家犯错最多的地方。我自己的习惯写法是int l 0, r 1e9; // 根据数据范围调整 while (l r) { int mid (l r) 1; if (check(mid)) r mid; else l mid 1; }这套写法维护的是“l是第一个可行解”的性质配合check函数“可行返回true”不容易出现死循环。初赛阶段掌握这一种写法就够了别在赛场上临时想边界。3.4 简单DP状态定义比转移方程更难初赛的DP题一般不会太难最常见的是线性DP和背包问题的变体。但很多新手栽在“状态定义没想清楚就急着写转移方程”。以最长上升子序列为例如果定义dp[i]为“以第i个元素结尾的最长上升子序列长度”转移方程就是dp[i] max(dp[j] 1)其中j i且a[j] a[i]。但如果定义成“前i个元素中……”就会变得模糊转移时容易出错。我的经验是做DP题的第一步是在纸上画出这个“状态维度”横轴是序列位置纵轴是额外限制条件比如背包容量、剩余次数。把状态定义写成一个完整的中文句子比如“dp[i][j]表示考虑了前i件物品、总费用为j时能获得的最大价值”。句子写对了转移方程往往就只是“从上一个状态怎么走过来”的自然描述。这里还有一个初赛经常考到的优化滚动数组。遇到二维DP时如果转移只依赖上一行的数据就可以把二维数组压成一维。但滚动数组的注意点在于遍历顺序。以01背包为例如果j从大到小遍历就保证了每个物品只被用一次而完全背包则需要从小到大遍历。这个“方向问题”是初赛DP题失分的重灾区写代码前一定想清楚。4. 比赛中的调试与时间管理那些容易丢分的细节4.1 快速读入一道题从超时到AC的距离初赛题目数据范围一般不会特别刁钻但也存在那种“用cin也能过”的林黛玉型题目和“用cin必超时”的关云长型题目。我建议从一开始就在代码模板里加上快速读入宁可每次多写几行也不要冒险。一个比较稳妥的模板是#include bits/stdc.h using namespace std; inline int read() { int x 0, f 1; char c getchar(); while (c 0 || c 9) { if (c -) f -1; c getchar(); } while (c 0 c 9) { x x * 10 (c - 0); c getchar(); } return x * f; }注意如果需要读入大量数据且题目单组测试点时间很紧用getchar手写快读是最稳的方案。如果数据量不大直接用ios::sync_with_stdio(false); cin.tie(0);提速后的cin也完全够用关键是不要关掉同步后又用scanf和printf混着读那样会产生不可预知的Bug。4.2 边界条件样例过了不等于能AC我帮别人看代码时发现初赛阶段最常见的错误是数组开小了。很多题目会有一个很“阴”的trick实际使用的数组下标从1开始但数据范围里说n ≤ 10^5于是有人开了a[100005]结果在输入n的同时还可能有q次操作数组至少得开a[100005 5]。多开5到10个单位的余量是我写的所有代码的标配。另一个边界陷阱是数据类型溢出。题目给你“结果对10^9 7取模”你在计算中间乘法时如果直接int x a * b % MOD而a、b本身已经接近MODint乘法溢出是必然的。养成习惯凡是有乘法运算且可能超过2^31一律用long long。初赛阶段我不太主张为了省时间刻意用int在评测机上多占点内存换来安心非常划算。4.3 调试策略让程序自己说出错在哪初学者调试通常是在代码里到处加cout看中间结果然后一遍遍提交。这种做法在初赛里非常浪费罚时。我更推荐三步走第一步用自定义的小样例。比赛题目给的样例往往偏弱自己构造几个边界样例n1、n最大值、所有元素相同、递增序列、递减序列等。这一步能过滤掉一半以上的逻辑错误。第二步用断言。在关键位置加上assert如果某个条件不满足程序就会立刻报错退出比肉眼检查哪些变量赋值错误高效得多。比如题目保证输入合法但你的程序在某个分支里访问了非法的数组下标assert(i 0 i n)能直接把问题暴露出来。第三步对拍。这个在备赛章节里提到过但比赛中如果实在调不出来我有时也会现场写一个暴力版本跑对拍。虽然会占用时间但总比反复错误提交积累罚时强。4.4 做题顺序先把能拿的分全拿到前面提到的题目难度梯度在实战中的具体操作方式是前30分钟把A、B两道签到题稳稳拿下。如果A题卡了超过15分钟大概率是读题出问题了立刻跳过去做后面的题回头再处理。30到90分钟集中火力做C、D两题。这两题是晋级的分水岭尤其注意先写出“最暴力版本”再优化哪怕暴力版本只能得部分分也一定要保证有个提交记录在那里。90到150分钟挑战E、F题。如果发现一道题完全没思路就勇敢跳到下一道。初赛不是要求全对而是要求“在有限时间内总得分最大化”这个观念一定要根深蒂固。最后30分钟回头看所有提交过的题确认没有细节遗漏尤其检查long long、数组大小和输出格式。5. 赛后复盘与通往复赛的进阶路线5.1 复盘的正确打开方式比赛结束后我通常会做一份“错题清单表格”把每道题的状态记录下来题目AC情况用时错误次数卡住的环节赛后补题收获AAC12分钟0无无BAC28分钟2边界条件输出结果前忘记换行CWA超时3二分判定函数对check的单调性理解不深D未做--无思路贪心堆的常规解法这张表的价值在于它能帮你看清自己失分的模式。是读题太慢是边界处理不扎实是某些算法套路没有形成条件反射我见过太多选手赛后只关心“排第几”“过几题”却从不去分析自己的薄弱环节这样比多少次赛进步都很有限。5.2 初赛到复赛的能力缺口如果你顺利通过了初赛恭喜你但复赛的难度会上一个台阶。初赛的“模拟基础算法”在复赛中会变成“高级数据结构复杂DP图论综合应用”线段树和树状数组几乎是复赛必考。从初赛结束到复赛开始这段时间我建议优先补三块内容第一线段树。从区间求和开始逐步理解懒标记和区间修改再到权值线段树。这是整个复赛阶段最重要的单一数据结构。第二数论进阶。扩展欧几里得、逆元、中国剩余定理在复赛中出现的频率不低初赛时如果只学了快速幂和质数筛这会是一个明显短板。第三动态规划进阶。状压DP和树形DP是复赛的常客建议在初赛结束后尽早开始练习因为它们的思维模型需要时间沉淀临时突击很难见效。5.3 关于初赛的一些个人体会回到标题这件事上。很多人觉得初赛只是“入门门槛”随便练练就能过但以我带过的人和自己的参赛经历来看初赛恰恰是最能暴露基本功的环节。那些平时刷题时忽略的输入输出细节、边界处理习惯、对时间复杂度的敏感度都会在初赛的赛场上被放大检验。我印象最深的一次经历是有个平时只刷难题的同学在一场初赛里因为B题的一个“数组越界”问题反复提交了7次最终虽然AC了但罚时已经被拖到离谱和另一道题未做出导致的排名滑落叠加直接无缘复赛。相反一位平时成绩中等的同学因为稳扎稳打合理分配时间把能拿的分都拿到反而以较好的名次晋级。初赛比的从来不仅仅是谁的脑子快更是谁的基本功扎实、心态更稳、策略更清醒。如果你正在备赛我希望你能从这篇文章里收获的不只是几个题型的套路而是一种“比赛思维”上场前把所有可能的不确定性都提前排除上场后把精力集中在解题本身而不是和评测机“搏斗”。把基本功打磨到不需要思考就能正确写出来的程度初赛自然会给出你应得的回报。