1. 今日选题:为什么我把时间花在这三题上
3月18号,又是固定刷题日。今天在洛谷上大概待了四个多小时,完整AC了3题,另外有2题只看了题解没动手写。先说结论:今天的核心不是“刷得多”,而是把之前一直没啃透的图论模板重新过了一遍,顺便借着一道新题复盘了一下自己看题解的习惯。
很多人刷题有个误区,觉得刷得快、刷得多就是好事。我现在的看法不太一样:选题比刷题更重要。洛谷的题库已经非常庞大,题目质量整体在线,但如果你不会挑题,很容易出现“刷了100题,水平还是原地踏步”的情况——这我太有经验了,前两年就干过这种蠢事,每天签到式地刷水题,成就感拉满,结果遇到稍微绕一点的题目照样卡死。
我选今天的题,核心看三个维度:难度标签、通过率、考察标签。难度标签决定今天大脑的负荷,通过率决定这道题有没有水分,考察标签决定它值不值得占用训练时间。今天的三道题分别承担了三种不同的练法,下面展开说说。
1.1 三道题的定位分工
| 题目编号 | 定位 | 难度区间 | 今日状态 |
|---|---|---|---|
| P6175 | 经典图论模板,最小环问题 | 普及+/提高- | 重写复习,不依赖题解独立AC |
| P8675 | 图论搜索题,涉及反向边预处理 | 提高 | 看了题解思路后自己实现 |
| P11230 | 编号较新的题目,考察方向偏综合 | 提高+ | 第一眼没思路,借助题解拓展视野 |
P6175是那种特别经典的模板题,考察的是用Floyd算法求无向图最小环。为什么今天把这种老题翻出来重写?因为我上周做一道题的时候,需要用到最小环的思想,结果发现自己只能背出模板,根本说不出为什么每次枚举中间点k的时候要先把环检测放在最内层更新之前。这种“会背不会讲”的状态,在平时没什么,一遇到变形题就完蛋。
P8675是一道需要处理反向边的搜索题。它的套路是先把所有能到达终点的节点预处理出来,再在这些节点的基础上做一遍BFS或DFS。这个“反向建图预处理”的思路很典型,在很多题里都能看到影子,尤其是类似“寻找道路”这种经典题。所以我今天特意找了一道这个方向的题目,目的就是把这个套路彻底吃透,而不是仅仅记住代码。
P11230完全是一个意外。刷题刷累了,随手点了题库里一个编号比较新的题,结果一看就不是善茬。这种题号很新的题目,通常出题人设计得比较巧,不会像老题那样有很明显的套路痕迹。我这道题的第一反应是完全没有思路,翻了题解才意识到自己把一个关键条件看漏了——这种“看漏条件”的教训,其实比AC本身更有价值,后面我会详细讲。
1.2 为什么今天没有全部开新题
我之前有一个很坏的习惯:刷题只刷新题,觉得做过的题再写一遍是在浪费时间。但实际上,把一道经典题隔一段时间重新做一遍,收获经常比做三道新题还大。
第一遍AC的时候,你可能是在“背”,甚至是在“猜”。比如Floyd最小环,第一遍可能是照着模板敲,A了就觉得会了。但第二遍要求自己不看任何资料,从状态定义、转移方程、环的检测时机一步步推出来,这个时候你才会真正意识到,自己对“为什么先检测环再更新最短路”这件事有没有理解到位。
我用今天的时间做了个对比:前一个小时,我先花20分钟把P6175重新写了一遍,AC之后没有立刻走,而是把Floyd求最小环的完整推导过程在草稿纸上画了一遍。然后剩下的时间去看P8675和P11230,感觉脑子比直接硬刷新题的时候清晰很多。刻意练习的本质不是反复做题,而是做“刚好够到但又不那么轻松”的题。
所以,今天的选题策略总结下来就是一句话:用一道经典模板题热身,用一道常规套路题巩固,再用一道陌生新题暴露自己的盲区。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 读题解的正确姿势
今天最大的意外收获,是在P11230上翻车之后,重新梳理了自己看题解的方法。说实话,我在洛谷看题解的历史非常长,几年前还是那种“看不懂就翻题解,翻完照着敲一遍”的状态,结果就是出现了一个极其诡异的毛病:同一个知识点的题,稍微变个样子我又不会了。
后来我才想明白,问题不是题解不好,而是我看题解的方法有问题。
2.1 读题解之前的三个前置动作
现在我对自己的硬性要求是:一道题如果20分钟内完全没有思路,可以先看一眼题解,但看之前必须完成三个动作。
第一,把题目里的所有约束条件抄一遍。这个听起来很傻,但特别管用。P11230这道题,我第一眼没思路,抄了一遍条件之后发现,题目里有个“保证所有边的权值互不相同”这种看似不起眼的条件,恰恰是解题的关键入口。很多时候,你卡题不是因为算法想不到,而是因为条件看漏了。
第二,把自己能想到的暴力解法写出来。哪怕你是用最蠢的枚举,也要写。这个动作的意义在于,当你看题解时,你能准确判断“优化”到底优化在哪一步——是剪枝,是换数据结构,还是彻底换了思路。没有暴力作为参照,题解里的很多操作你是看不出来它为什么要存在的。
第三,记录自己的原始困惑点。比如“我不知道这个状态怎么转移”“我不知道这个图要怎么建”。带着具体的问题去看题解,比漫无目的地翻效率高很多。
这三个动作做完,才允许打开题解区。
2.2 什么情况下我应该直接放弃看题解
有一种情况,我建议直接放弃思考、尽快看题解。就是当你发现这道题涉及的知识点自己完全没见过,甚至题目里给的算法名都不认识。
比如P6175这个最小环问题,如果是一个没学过Floyd的人,你让他想破脑袋也很难想出那个“枚举中间点k,检测dist[i][j]+e[i][k]+e[k][j]”的公式。这不是智商问题,是知识盲区问题。知识盲区不存在“灵光一现”,只看你脑子里有没有那个知识储备。
那怎么判断是知识盲区还是思路卡壳?我的标准是:能说出题目在考什么方向,只是想不到具体的做法,属于思路问题,可以再坚持一下;连题目在考什么都说不出来,那大概率是盲区。
刷P8675的时候,我能明显判断出题目是搜索题,但没想到要先“反向建图+BFS预处理”,这就是思路卡壳,所以我允许自己再看几分钟。而P11230我连要从哪个方向入手都判断不了,就直接看题解了,没必要在未知区域空耗一个晚上。
2.3 从题解区真正要带走的三样东西
看题解不是把作者的代码复制到剪贴板再粘贴到编辑器里。我从题解区带走的东西,严格来说是下面这三样:
第一,状态定义和转移思路。这是题解的核心价值,你要搞清楚作者用dp[i][j]表示的是什么状态,为什么可以这样定义,转移的时候为什么是这种顺序。比题目本身重要得多。
第二,优化动机。比如P6175的题解里,很多人会写“先判断环再更新最短路”。你看的时候就要问为什么是这个顺序,如果反了会怎样。真相是:更新最短路会把当前k作为中间点的最短路径提前算好,导致后面计算环时把不是环的路径也算进去。这个“为什么”搞懂之后,就算把代码忘了,也能凭逻辑推出来。
第三,实现细节和坑。比如BFS时要不要记录父节点,Floyd初始化时INF值应该取多大。这些细节通常在代码注释里,但它往往决定你能不能一遍过。
我记录题解的方式是:在洛谷题目讨论区里点“收藏”,然后在自己的笔记软件里整理成“题目+核心思路+两三个关键坑”的格式。不粘大段代码,只写逻辑和推导。
提示:看到题解之后,一定要合上题解自己重新写一遍。如果合上题解还是写不出来,说明你刚才看的是“别人的代码”,而不是“别人的思路”。这种时候重复看没有意义,你需要的是隔一天再回来重写。
3. 洛谷的几个高频功能细节
这个月我在洛谷上除了刷题,其实还有不少时间花在捣鼓平台本身的功能上。今天有几个细节想顺手记一下,都是些搜索引擎里天天有人问、但没人系统讲过的点。
3.1 切换编程语言到底怎么切才不踩坑
“洛谷如何切换编程语言”这个问题,几乎每周都有人问。其实入口很简单:题目提交页面右下角,有一个语言下拉框,默认是你上次用的语言。但问题往往出在“默认语言”和“代码模板”上。
很多新人经常会遇到一个诡异的情况:上一次用C++写的代码模板,这次想切到Java提交,结果编辑器里还是一堆C++的#include,提交时又忘了改语言,最后整页的CE(编译错误)。我自己的习惯是:在“个人设置”里把默认语言改成常用语言(我的是C++17),然后每个代码模板只在对应的编辑框里用。另外还有一点,洛谷支持在单个题目页面的提交框里临时换语言,它会重新加载你的模板,但如果你用的是同一个浏览器窗口,它有时候会缓存旧模板,这时候刷新一下页面就好了。
切换语言还有一个冷门小技巧:当你准备提交Java代码时,类名一定要写成Main,否则必定编译失败。而Python则要注意洛谷的Python版本是3.x,个别老题可能默认Python2。如果提交Python超时,建议看看是不是用了递归,洛谷对Python的递归栈限制很不友好,通常要手动设置sys.setrecursionlimit,或者直接用非递归写法。
3.2 团队系统加C++题单:给被人拉着组队的人一些建议
洛谷的“团队系统”很多学校都在用,用来布置课后作业和小组训练。如果你是被拉进团队的人,大概会接触两类东西:团队题单和团队作业。题单是一组题目的合集,作业则是老师指定的一批题目,通常有截止时间。
我自己的看法是,团队题单对自律差的人来说特别好用。因为题单通常按难度递进,你不用自己想“下一步刷什么”,无脑跟着做就行。但要注意一个问题:团队题单里的题目难度并不一定适合所有人。有的人可能是被队员拉进团队的,进去一看全是提高+的题,那完全没必要硬啃。
这时候我建议利用“搜索用户”功能,去团队里那些AC率高、等级高的成员主页,看看TA的提交列表,了解一下同一道题TA是怎么组织的。也可以看看TA关注的题单,往往能挖到不少隐藏的优质资源。这比自己在题库里漫无目的地翻效率高很多。
3.3 搜用户、看大佬提交历史的正确用法
洛谷的“用户搜索”不只是用来查人的。我发现一个很实用的玩法:搜到某个水平很契合自己的用户,点进TA的主页,看TA最近一周提交过什么题、哪题AC、哪题还在WA。这相当于一个“活体的刷题进度表”。
为什么会有人去看别人的提交历史?因为高水平用户的WA记录比AC记录更有价值。你看到TA在一道题上反复WA了好几次,说明这道题一定有坑,你可以提前预防。另外,看大佬的“做题推荐”没有太大参考价值,因为大佬的难度曲线不适合普通人。
我自己收藏了几个水平中等偏上的稳定用户,隔三差五看看TA们在刷什么题。这个习惯持续了半年,明显感觉自己的选题效率提高了很多,不怎么在无意义的题上浪费精力了。
4. 今天踩过的坑和排查思路
今天做题的过程里遇到几个经典问题,一个是本地跑得好好的,交上去全WA;还有一个是C++代码在洛谷上直接爆栈。这里把排查过程记一下,都是那种“你百度不一定能搜到答案”的实操经验和细节。
4.1 本地AC、提交全WA的三个高频原因
先说第一个:数组初始化问题。本地编译器有个特性,全局数组默认初始化为0,但如果你写的是局部数组,比如在main函数里开了一个int a[N],它的初值是未定义的,在本地可能碰巧是0,但洛谷的评测环境不一定。很多时候本地AC是因为局部变量恰好没被污染,提交上去就各种怪异的错误。解决办法很简单,全局数组不要吝啬内存,随手int a[N] = {0},或者用memset。
第二个是输入输出格式。本地测试的时候你可能会手动输入样例,多了一个或少了空格都无所谓。但评测机是严格比较字符的。你这边的格式只要差一个空格,甚至多换一行,都会WA。所以建议不管什么题目,统一采用“按字符或者按类型逐项读取”的方式,别在字符串里手工加空格。
第三个是多组数据没有重置变量。很多题都是多组测试数据,很多人只在读入第一组数据时正确,第二组开始就会保留上一组的残留状态。排查方法是在循环开始时把所有变量重新初始化。
4.2 数组开小与递归爆栈
今天在P8675上就踩了数组开小的坑。题目给定的节点范围是1e5,我第一次开了一个100005的数组,觉得够了。结果提交后大约3组数据后出现RE,后来一看才知道,自己建了反向图,每条边实际上是存了两遍,数组应该开到2*100005以上。这是我犯了无数次的低级错误,每次做题都要提醒自己:“开数组之前,先算清楚每条边会被存几次。”
递归爆栈的问题,C++里经常遇到。洛谷的递归栈深度不算大,如果你DFS递归层数达到几万层,很容易爆。我的习惯是:能用BFS就用BFS,如果一定要递归,就用“显式栈”替代递归。还有一个小技巧,如果今天只是练习,不对效率太苛刻,可以在main函数里加一句std::ios::sync_with_stdio(false)和cin.tie(0),能避免很多莫名其妙的输入输出超时问题。
4.3 超时不一定是算法复杂度差
很多人一遇到TLE(超时),立刻觉得是自己算法复杂度不行,然后开始重构整个思路。其实很多时候,TLE是实现的细节问题。
我今天遇到一个情况(这里不说具体题目了,类似情况的通用排查思路):算法复杂度O(n log n),理论上完全没有问题,但还是超时了。排查到最后,发现是循环里有一个不必要的排序操作,排序本身的复杂度是O(n log n),但因为在循环里执行了多次,整体就变成了O(n^2 log n)。这种“隐藏的复杂度”在刷题时很常见。
排查TLE的标准流程,我总结为三步:第一步,用极端数据在本地测试,看运行时间;第二步,检查有没有隐藏的循环内复杂操作(排序、嵌套遍历、字符串拼接);第三步,检查是否因为频繁动态分配内存(比如vector反复push_back导致多次扩容)。这三点都排除了,再考虑换算法。
5. 这周刷题数据与下周调整
顺手把这一周的刷题数据整理了一下。
| 指标 | 本周数据 | 上周数据 | 变化 |
|---|---|---|---|
| 总AC题数 | 14 | 11 | +3 |
| 其中新题 | 9 | 9 | 0 |
| 重写旧题 | 5 | 2 | +3 |
| 看题解后AC | 4 | 6 | -2 |
| 平均每题调试时长 | 18分钟 | 26分钟 | -8 |
从数据能看出一个明显变化:这周“新题”没增加,但总AC数多了3题,而且看题解后AC的题数降了。原因就是我周中调整了策略,把一部分时间用来重写经典模板题,比如今天早上的P6175。这种策略的直接收益是,模板熟了很多,做新题的时候不再需要频繁翻题解回忆基础代码。
下周我打算继续沿用这个节奏,但会把“重写旧题”的比例控制在一半左右,另外拿一半时间专门去攻克自己薄弱的方向(目前是DP优化和构造题)。数据结构里的线段树,我准备用一周时间把洛谷上的“线段树题单”走完。这个计划不一定适合所有人,但如果你发现自己也一直卡在新题看不懂、旧题全忘光的状态,可以参考一下。
另外特别想提醒一句,刷题不是越苦越好。我不止一次见过有人刷题刷到凌晨两三点,第二天上课全是黑的。刷题的收益曲线其实有个瓶颈,每天的有效投入大概在4到6小时,超过这个量之后,再做新题基本就是无效输入,还不如早点睡,第二天起来再想。
最后再分享一个小技巧:洛谷上有一类“小游戏”式的题单,比如一些纯思维题、脑筋急转弯题,难度不高但特别有意思。我一般会在连续刷难题卡壳的时候,挑一两道这种思维题来转换状态,既能让脑子放松,又不会中断刷题的手感。这道“Switch The Lamp On”差不多就是这个类型,属于灯开关类思维题,不需要复杂数据结构,但非常考验逻辑推理的完整性,适合用来做刷题中场休息。它不会告诉你“这个套路”,但你顺着题意自己推,很容易上瘾。这也算是洛谷题库一个不太容易被注意到的宝藏品类。
