
刚结束了SMU-ACM冬训营第一周的节奏我趁着晚上把这一周的数据、讲课记录和队员们的反馈整理了一遍。这篇周报不打算写那些“大家都很努力”之类的客气话直接把这一周里我观察到的东西、题目设置的逻辑、踩过的坑和下一周的调整方向都摊开来讲。如果你是准备2026赛季的ACM选手或者正在带队伍做类似的冬训安排这篇内容应该能给你一点可复用的参考。1. 冬训第一阶段怎么设计三条线并行而不是盲目刷题1.1 2026赛季的时间窗口决定了冬训的定位先说一个很容易被忽略的问题冬训到底在为哪个时间点服务很多队伍把冬训当成“随便练练”等到了赛季前再发力这是我对SMU-ACM过往成绩复盘后最想改变的一点。仔细看2026年的赛季日历真正的大赛密集期在下半年的区域赛、省赛和ICPC/CCPC的邀请赛而从现在到春季其实是整年里面最完整、最没有课程压力的连续时间块。这个窗口一旦错过开学后各门课业、实验、考试会像洪水一样把训练时间撕碎到时候再想系统性补基础几乎是不可能的。所以冬训的核心目标不是“刷够多少道题”而是完成两件事第一让所有队员在算法基本功上达到一个统一的、可靠的下限第二筛选出真正适合继续高强度训练的种子选手。第一周的课表就围绕这两个目标来排不追求难题也不让任何人闲着。1.2 三条训练线专题课、每日题单、周末模拟赛我把整个冬训设计成三条并行线专题讲授、每日训练题单、周末限时模拟。这三条线各有各的职能不能被互相替代。专题课解决的是“这个算法是什么、适用场景是什么、复杂度怎么算”的问题。每天上午固定一个半小时的讲解从人脑的记忆规律来说单次灌输太多新概念效果会很差所以我把每个专题拆成两天第一天讲原理和标准模板第二天用一个完整题目把模板推向变形应用。每日题单解决的是“用手记住”的问题。只听课不做题等于白听ACM是个熟练度极其重要的活动很多选手在赛场上卡住不是因为不知道算法而是因为想不起来用哪个、写出来的代码总是差一点边界条件。我要求每人每天至少提交四道题难度梯度是基础题两道、进阶题一道、挑战题一道。周末模拟赛解决的是“在压力下决策”的问题。平常做题可以慢慢想、随便调试但比赛不行。周末的限时训练就是从第100分钟、第150分钟这种真实赛感中帮队员找到自己的节奏和短板。第一周我们没有用太难的题重点看的是临场读题、代码实现速度和卡题之后的反应。1.3 周报机制的意义数据比感觉更诚实周报不是用来发在公众号上好看的它本质上是训练数据的可视化。我会逐个记录每个队员的提交数、通过数、补题数、每道题的第一次AC耗时、WA次数。这些东西单独看没有意义放到一起去分析就能看出很多奇怪的问题有人刷题量很大但AC率只有20%说明他做题太快太糙检查习惯没建立有人AC率高但速度极慢说明他是靠反复试错而不是一次写对还有人不做挑战题永远待在舒适区里面这一类是最难带的。这些数据也直接决定第二周的题目分组。基础薄弱的队员继续在排序、栈、队列这些板块上打磨已经掌握得很牢的队员我不会让他们浪费时间做重复题而是提前进入二分答案、前缀和、差分这些稍具思维量的专题。2. 第一周核心专题拆解从“语法正确”到“复杂度敏感”2.1 快速输入输出与STL基本功看似简单实则最容易翻车第一周的专题从快速IO开始讲很多新人会觉得这是个特别无聊的话题但第一次模拟赛就有人因为没写ios::sync_with_stdio(false)导致TLE。这道坎其实不是输入输出本身的问题而是“算法复杂度意识”的第一课。一个O(n)的读入过程如果被cin拖慢到和O(n log n)的算法一个速度那后面想卡时间就很被动了。我给他们总结了一个基本约定竞赛代码一律用ios::sync_with_stdio(false); cin.tie(0);开头如果题目输入量超过10万行或者涉及大量字符串直接考虑用scanf/printf甚至自己写快速读入。这不是说cin就一定不行而是ACM赛场上容不得任何不确定因素能稳定复现的选择才是最安全的选择。STL部分我讲了vector、stack、queue、deque、priority_queue、map、set和algorithm库里的常用函数。有时候我会觉得STL是ACM里性价比最高的知识点它不涉及多么高深的算法但用得好可以省下一大堆手写数据结构的时间。比如lower_bound解决有序数组的查找问题unique配合erase去重sort的第三个参数写自定义比较器priority_queue处理贪心和最短路问题这些写法必须达到不需要过脑子的程度。我反复强调一个习惯每道题的代码里STL的选用应该和算法设计同步发生而不是写完主体逻辑再回头补容器。比如拿到一个需要维护当前最小值的题应该立刻想到用单调栈或者堆根本不需要在vector上手动搞什么前缀最小值数组。2.2 排序与二分从“会调用函数”到“理解背后的复杂度模型”排序是这一周的重点专题但我讲的不是怎么调用sort而是排序在算法设计中的“结构性作用”。在竞赛里排序往往不是目的而是把无序问题转化为有序问题的手段。很多题目如果不排序暴力做法是O(n²)排个序加两个指针扫描就能优化到O(n log n)很多查询问题如果不排序就得每次遍历全部数据排序后配合二分查找就能大幅加速。我用了逆序对问题来带着他们过了一遍从暴力到优化的完整推导。暴力写法是两重循环统计前面的数比后面的数大的对数时间复杂度O(n²)数据量到10万级别就完全跑不动。优化做法是把逆序对计算放在归并排序的合并过程中每次合并两个有序数组时如果右侧指针指向的元素小于左侧指针指向的元素那么左侧数组中当前指针右侧的所有元素都能和这个右侧元素构成逆序对累加数量即可。这样归并排序本身O(n log n)的时间复杂度就顺便完成了统计是一道经典的“排序本身即算法”的例题。二分部分不止讲了在有序数组里查找某个值我更强调的是“二分答案”这个思维模型。它适用于“当答案具有单调性时可以把求解问题转化为判定问题”的场景。比如给了n个数要求分成k组每组和的最大值最小化这种题目直接求很难下手但如果二分这个最小值x问题就变成“判断能否在每段和不超过x的前提下分成至少k组”这就变成一个简单的贪心检验时间复杂度O(n log sum)。第一周涉及的不深只是把二分答案的框架搭起来后面在更多题目里会反复用到。2.3 单调栈与单调队列打破“暴力循环”的第一个思维升级第一次讲单调栈我就和队员们说这是你们从“什么都想两重循环”过渡到“寻找数据内在规律”的第一个关卡。暴力做法在绝大多数情况下是能想到的问题在于复杂度撑不住。单调栈的核心思想其实非常朴实在处理序列时保持栈内元素的有序性利用“每个元素只会被入栈和出栈各一次”的性质把原本需要反复扫描的整体计算变成每个元素的O(1)处理。我用了经典的“柱状图中最大的矩形”来作为案例。题意是给n个非负整数代表柱子的高度求这些柱子能形成的最大矩形面积。如果暴力枚举左右边界再找最小高度复杂度是O(n³)或O(n²)。用单调栈的做法是从左到右扫描每个柱子维护一个递增的栈当遇到比栈顶柱子低的柱子时说明栈顶柱子的右边界已经确定了这时就可以计算以它为最小高度的矩形面积——左边界是它出栈后新栈顶的位置右边界是当前柱子的位置宽度就是两者之间的距离高度就是该柱子的高度。出栈过程把所有可能的“以某根柱子为最小值”的矩形都计算了一遍每个柱子只进出栈一次整体复杂度O(n)。这里要特别说明一下容易错的地方边界条件。计算宽度时左边界是栈空 ? -1 : 新栈顶下标右边界是当前下标i所以宽度是i - left - 1。很多新人会写成i - left或者忘记考虑栈空的情况结果WA得莫名其妙。我让每个队员都手推一遍这个小例子而不是直接背代码就是希望他们真正理解单调栈为何能保证正确性。2.4 搜索入门DFS、BFS的模板与变形第一周的最后一个专题是搜索。DFS对于新人来说最难的其实不是递归本身而是“状态的设计”。一个BFS问题里的状态是坐标加步数一个DFS回溯问题的状态是当前路径和剩余可选元素如果状态定义不清晰代码就很容易写得一团乱麻。我带着他们做了一个全排列的DFS框架然后延伸到了组合枚举重点讲了vis数组和回溯时状态恢复的问题。BFS部分我讲了最短步数的网格模型强调队列先进先出的特性天然保证了第一次到达某点时的步数一定是最少的。同时也提醒他们BFS不一定只能用在二维平面上抽象图、隐式图比如骰子的状态转移、八数码问题都是BFS的用武之地。第一周只需要掌握裸BFS的写法变形和应用放到后面专门专项做。3. 实战记录第一周题单背后的选题思路3.1 基础题把排序、二分、STL变成肌肉记忆第一周的每日题单基础题我选得很“朴素”但每条题目都各有针对性。比如有一道“给n个数求第k大的数”最简单可以用排序后直接索引输出也可以使用nth_element我要求他们至少写出两种实现这能逼着他们去理解不同复杂度背后的适用场景。还有一道“合并两个有序数组”这题的考点是把归并排序中的merging过程单独拎出来练很多新人会把两个数组直接拼起来再sort虽然也能过题但我就问一个问题“如果内存不够用怎么办”答案就是O(1)额外空间的双指针合并。这道题的引申意义在于归并排序、逆序对、外部排序、甚至链表合并底层逻辑全是同一个双指针模板。这组基础题里我最看重的是“链表模拟”那道题。给定一系列操作往链表指定位置插入或删除节点要求输出最终所有节点。用数组模拟链表或者直接用list都行关键是不能真的在每次操作时通过遍历找位置就完事了必须理解“位置”这个概念在链表里是需要通过遍历来维护的。很多人写数组模拟链表时搞不清prev和next之间如何连接老是把两条链串在一起写乱这道题正好暴露问题。3.2 进阶题单调栈和前缀和的第一次实战进阶题我用了“每日温度”这个经典的单调栈应用题。题意是给定每天的温度数组要求输出每个位置需要等多少天才能等到比当天温度更高的日子。如果暴力对每个位置往后扫描最坏O(n²)数据量大就会TLE。单调栈的思路是维护一个递减栈当遇到比栈顶温度高的新元素时把栈顶元素弹出并计算它和新元素下标的差值作为答案。这道题对新人来说有个很好的体验过程先用暴力写上几分钟提交发现TLE再用单调栈重写AC之后对比两份代码的耗时这才知道优化不是虚的。我让他们在本地用10万随机数据测两组写法的时间直观感受O(n)和O(n²)差了多少倍。这不是浪费时间是让他们建立复杂度敏感的最便宜的方式。进阶题里我还放了一道前缀和的“子数组和等于k”的变种。前缀和本身不复杂但配合哈希表map去统计“之前出现过的前缀和值”这件事就带有一点思维量了。核心公式是如果当前位置的前缀和为sum[i]要找某个区间和为k只需要看之前有多少个位置的前缀和等于sum[i]-k用map维护这个计数即可时间复杂度O(n)。这道题的难点不在算法本身而在于明白为什么要用前缀和来转换问题视角。3.3 挑战题多源BFS与状态压缩的“开胃菜”挑战题我没有出太夸张的难题选了LeetCode高频的“01矩阵”作为多源BFS入门。题意是给一个01矩阵求每个位置到最近的0的距离。最容易想到的思路是对每个1分别做BFS但这样会重复计算很多次复杂度是O(n²·m²)级别。正确做法是把所有0的位置同时放进队列作为BFS的起点然后逐层向外扩展每个格子第一次被访问到时的层数就是它到最近0的距离。这道题的核心价值是让队员理解“多源BFS就是多个起点同时开始扩散队列初始就有多个元素”这对他们后面理解图论里的最短路算法也有帮助。另一个小挑战是状态压缩的入门我给了一道“用一个整数的二进制位来表示一组开关状态”的题目。状态压缩和DP没什么关系但它是很多高级算法的基础先让他们把“二进制表示状态、位运算修改状态”这套东西弄熟后面做状压DP就会顺畅很多。3.4 补题模式与代码规范这周的“隐藏课程”说实话ACM训练里非常容易被忽略的一环是补题。很多队员比赛或者做题做完了看见AC了就觉得自己掌握了一个星期后回头再写照样边界写错照样不会做。我要求的补题不是简简单单把正确答案抄一遍而是至少要在AC后的24小时内重新独立写一遍而且不能看自己之前的提交。如果第二次写还是犯错说明这个知识点根本不是真懂需要回去重新看题解和原理。代码规范我们这周也开始抓了。变量命名要能看懂数组开多大要有明确依据而不是瞎猜每份代码的复杂度分析要能说出来至少能回答“这个算法为什么是这个复杂度”。我不会强制统一模板但是我强烈建议每个队员建立自己的“个人模板库”把常用代码段比如快速读入、排序比较器、BFS框架、二分模板统一整理在一个文件里。比赛的时候直接用熟悉的手感去敲比现场临时想怎么写要快得多也能减少低级错误。4. 队员常见问题排查这周踩过的坑我一条条写下来4.1 TLE到底是怎么回事别急着怪机器慢这个星期被问得最多的一句话就是“老师我本地跑得挺快为什么交上去TLE了”。这个问题十个有八个其实是复杂度爆炸剩下两个是输入输出没优化或者死循环。我要求队员拿到TLE之后不要瞎猜按这个顺序查先看是不是某一组极端数据导致的最坏复杂度再查代码里有没有边界写错导致死循环最后再查IO速度。如果这些都查完了还是卡时间那就要考虑换算法思路。有一个很典型的案例有个队员写了一道单次查询的题用了在线段树去做每次查询O(log n)但他没注意到有高达十万次查询总复杂度其实是O(n log n)理论上能过。问题出在他把建树过程也写得很粗糙每建一个节点就递归一次大量递归调用频繁访问堆内存导致常数巨大。我们优化成用数组存线段树后速度立刻上来了。这提醒所有人复杂度不是只看Big-O级别常数和实现方式同样是性能的一部分。4.2 边界条件和数组越界WA的第一大来源第一周统计下来WA次数最多的原因不是算法不会而是边界条件处理不对。二分查找的left和right取值到底是开区间还是闭区间很多人写的时候不统一同样的题这次AC下次WA。单调栈里的空栈判断BFS里坐标是否越界的判断条件前缀和里下标从1开始还是从0开始这些都是细节但全是致命细节。我的建议很简单每个数据结构的操作脑子里先过一遍“如果这是空的情况”“如果只有一个元素的情况”“如果正好在边界上的情况”这三个special case。写代码之前先用几秒钟想清楚边界而不是写完代码后在几十个WA里慢慢试——后者不仅浪费时间比赛时还会产生很大的心理压力。用一个具体的例子求连续子数组最大和问题标准的Kadane算法是不断累加并把答案取最大值但如果数组全是负数呢如果初始答案是0那最后答案就是0这就错了答案应该是数组中最大的那个负数。类似这样的小陷阱光靠记忆模板是避不开的必须理解为什么这么写才正确。4.3 卡题卡太久怎么办时间分配是比赛的一部分周末模拟赛里我观察到很多队员在某一题上死磕了快一个小时导致后面会做的简单题都没时间写完。这是ACM新人最常见的错误——他们把“把这题AC”看得比“拿到尽量多的分数”更重要。真实比赛里时间本身就是资源合理的策略应该是开场前20分钟把四道题全部读一遍有个大概印象先用最简单题建立AC的节奏感和手感简单题在40-60分钟内必须全部解决然后马上转向中等题中等题如果思考超过20分钟还没有明确思路不要硬刚转去做另一道题用大脑后台默默处理挑战题是锦上添花不要让它占据主场时间。我还让他们养成一个习惯比赛时如果卡题超过15分钟切出去做一道简单题或者看一道新题往往换个视角回来再看原来的题思路就通了。这不是玄学而是因为大脑长时间固定在一个思维路径上会陷入局部搜索强行继续往往只会越陷越深。4.4 独立思考和讨论的边界别上来就问题解冬训的时候我最怕的不是队员不会做题而是他们一看题没思路就马上打开题解或者在群里直接问答案。ACM训练的核心能力恰恰是“在已知算法库的前提下面对新题独立思考并选择合适算法”的能力这个能力只有靠自己在脑子里挣扎才能练出来。我制定的规则是一道题如果没思路至少要自己尝试三种不同的切入角度至少写一版能跑通的暴力解来观察规律然后再去问队友或者看题解。看题解之后不是看完就完了必须复盘一遍自己为什么没想到是自己的知识盲区还是分析不到位。这样的复盘价值远大于多刷几道题。5. 第一周数据复盘与下周调整方向5.1 训练量完成度与质量评估第一周结束时我汇总了一下数据每日题单的整体提交率在80%左右AC率大概在55%补题完成率接近70%。这组数据对一个刚开训的队伍来说只能算合格不能算优秀。提交率不足来自部分队员还没有进入状态但更关键的问题是AC率低于我的预期——说明做题太急、检查太少的问题普遍存在。我更在意的是另一个数据有接近四成的基础题第一次WA后队员自己调试后能独立AC但两成的人会在同一道题上反复WA超过五次这类队员的问题通常是定位不到bug。这是思维严谨性的差距需要在后面的训练中专门训练我会要求他们在每次WA后先写下一句话说明“我改了什么、为什么改这个”不允许盲目提交。5.2 下周专题预告与人员分组调整第一周专题里每个队员的吸收情况其实差得挺多。我根据周报数据把队员分成了两组A组在基础数据结构上已经比较熟练第二周将进入动态规划初步、前缀和与差分、双指针、位运算基础B组则还需要在类模拟和基础算法上多巩固我会把单调栈、BFS的基础题再多配一些同时加入中等难度的二分答案题目。第二周的专题设定是动态规划的入门模型斐波那契类、背包类、线性DP、前缀和与差分的综合运用、双指针与滑动窗口、并查集初始。这些都是“听起来很基础但是实际赛场上高频出现”的算法也是从第一周的学习能力应用迈向算法思维提升的关键过渡。第二周的周末模拟赛我准备选一套难度略高、题型更杂的题重点观察队员在陌生题目面前能不能快速定位题目类型而不是猜算法。这一项能力如果能在冬训期间打磨出来后面到了真正的赛季才会从容很多。5.3 给队员和带队人的一些个人体会这一周训练下来我个人最大的感受是冬训最怕的不是队员水平低而是他们带着“自己水平低所以多刷点简单题就行”的心态在混日子。ACM里面的进步从来不是线性刷题刷出来的而是通过一轮又一轮“学个新算法、做几道变种题、复盘反思、再学新算法”的螺旋式循环慢慢抬起来的。如果每天都停留在自己会的领域里重复劳动那就是舒服地退步完全没有意义。对带队人来说我也想说一句话不要用题量衡量训练效果更不要把所有时间都用在讲题上。真正的成长发生在队员独自面对题目时的思考过程里。好的训练体系是提供方向、节奏和反馈而不是替队员把他的脑子用掉。第一周的沉淀让我看到了一些队员的潜力也看到了很多队伍的通病好在这些都是通过调整可以解决的。冬训还有很长一段时间能把这个地基打得多牢固直接决定了2026赛季这支队能走多远。