Re:从零开始文化课日常的第一集

背景的一

2 月 12 日,仅仅大年初三,林导就开始使用被称为“设置模拟赛”的教练专属的特殊技能,隔着上百公里,将我在 7 点 30 不到就从床上拉起来参加线上比赛了。钻石教练级别的强者,恐怖如斯!

打了两日线上模拟赛,到 14 日时,林导便把我们叫回学校,说是开会。会上则是大批特批什么“有些同学借口感冒,不参加模拟赛”,声称“感冒是不可能好的,我从重庆回来就没好过”,“现在这个时代,谁没有一点轻微感冒”。可是中午吃饭时,我才私下听说,所谓的“有些同学”的“轻微感冒”,实则是去医院输液这么“轻微”。

不过考虑到之前新冠大流行时,他作出过诸如“哪里没有病毒?只有太阳上才没有病毒呢,家不住在太阳上的同学都要回校训练”之类的论断,这个“感冒是不可能好的”,似乎也显得合理了起来。

次日,林导又要安排模拟赛。不过我妈正好在这一天预约到了参观松山湖研究所的票,再加上比我高一届的,去THU读预科 lc 也还在放寒假,我便向林导请了一天假,和 lc 一起去参观松山湖研究所。

我本以为林导会十分不满,乃至于百般阻挠,可出乎意料,林导居然爽快地同意了。我和 lc 一见面,lc 便问我使用了什么魔法,让平日连“去医院输液都不能模拟赛请假”的林导,答应我出来玩一天。我当然也毫无头绪,只能和他面面相觑。

参观过程中,我们默契地对近在 3 月 2 号的省选闭口不谈。可参观完后回来的路上,回想起 NOI 冬令营和广东省重点中学赛时的惨剧,我忍不住开口问他:“我该什么时候停止思考正解,转去写暴力?”

“当你想了十五分钟,觉得没有思路时就去写暴力”,他简单地回到道,“尤其是你觉得你在胡思乱想碰运气的时候”。这句话我已经听不同的学长讲了很多遍了,可是这一次听却格外觉得醍醐灌顶。

确实,只有这种做法才是最理性的选择。回想起我在广东省重点中学赛时,第一天一道题都不会,我当时心下暗想:“第一天第一题还是要做出来的吧,不然也太丢人了”。最后第一题不但没做出来,三道题的暴力分也没拿到,总共 300 分只得了 20 分。下午一看,却发现大部分人其实也没有做出任何一道题,只要把暴力写好就是前几名了。

至于 NOI 冬令营时,则是误以为会了第三题,实则我只是想了一个假做法,研究了 3 个多小时才发现做法的错误所在,连第二题的文件都没来得及创建,最后自然是连铜牌都没有拿到。

忽然间我还想明白了一个重要的问题:“为什么林导答应我出来玩一天?”,恐怕因为也只因为我是出来找 lc 玩吧。

临走前,lc 还送了我一盒糕点零食,其中有几类十分好吃。

咒语的一

2 月 11 日,我感觉很不好。一想到不到一个月后就将到来的省选,我就烦躁的想要踢垃圾桶。还好我房间内的垃圾桶是铁做的,不会像塑料垃圾桶一样开裂。

晚上躺在床上,这一赛季比赛时的场景几乎是历历在目:“广州市赛、CSP、NOIp、广东省重点中学赛、THUPC、THUWC、NOI 冬令营……”,每一场我都考的不好,完全没有表现出一个省队选手的水平。唯一一场相对令人满意的 THUWC,也是在第一天考砸之后,依靠第二天工程题 400+ 翻盘的。在不考工程题的省选里,怎么看我都毫无胜算——何况还是在 NOIp 已经落后了队线 60 分的情况下。

绞尽脑汁,我唯一想到的,我优势明显的比赛,竟然还是前年(2022 年)的广州市赛。那场比赛虽然我不会其中的难题,但是很多高水平选手在第一题结论出错而得了 0 分,最后莫名奇妙的拿到了很好的名次。

看着窗外的星星,我只好默默的许愿:“今年的省选的第一天的第一题是一道细节很多的结论题,很多人都在这个结论的细节上犯错了,但是我的结论完全正确。”

咒语的二

2 月 16 日,在前一天和 lc 参观完美丽的松山湖研究所之后,心情十分好,连早上的模拟赛打得也顺利了起来,很轻松就过了 2 题,甚至排行榜上的名次超过了雅礼的 lcm。

“要是省选也像这样轻松就好了”,我心想,“希望今年的省选每天都有能过的题”。

背景的二

2 月 11 日,江西的 lyf 邀请我一起打 CodeForces 上的一场 Div2 的比赛(即 Codeforces Round 924 (Div. 2))。这场比赛的时间很好,是下午五点开始的,我便答应了。赛前,lyf 说他最近状态很不好。话虽如此,可他最后至少还是做出了 5 道题,而我被 C 题卡住,最后只来得及做出 4 题。

一想到江西竞争压力小的多,而广东却是不然,连 NOIp 满分的人都有约 10 个,心情便很不好。

flag 的一

2 月 19 日,正式开学了。一向对竞赛生很有意见的级长,又授意班主任让我每天参加被称为“晨跑”的,对人类身体和心理的恐怖折磨,幸好班主任只是在微信上说了两句,睁一只眼闭一只眼,没有采取任何实质性的行动。“这还不是因为我一直没有表现出很强的实力导致的”,我想,“只要我进了省队,一切就会好起来的!”

背景的三

2 月 17 日,lc 又在 QQ 上找到我了,说是本来想回学校给我们讲一次课的,但是由于肥硕的极力反对,最终还是不讲了,然后发给我一个题单,说是本来计划要讲的题:

  • AGC017 Zigzag
  • CFgym104337B Mode
  • LG-P6811 「MCOI-02」Build Battle 建筑大师
  • USACO20DECPlatinum Sleeping Cows
  • LG-P7842 「C.E.L.U-03」探险者笔记 III
  • THUPC2023 百合
  • THUPC2023 老/虎/机 (添加斜杠以绕过敏感词屏蔽)
  • CF1608F MEX counting
  • ExPR #1 下降
  • LG-P8554 心跳

原来是全是动态规划方面的题目,我便暗暗下定决心,一定要在省选之前把这些题做一遍。可是白天还有林导的模拟赛,下午是模拟赛讲评,于是便只能晚上做了。

可是等到了晚上,我却忽然没有了白天的下决心时的动力。可能是我用的鼠标无线鼠标,很容易被攻击的缘故,不知为什么,一到晚上,鼠标指针就自动打开了网址收藏夹里的“BiliBili”,我心中着急的大喊“偏了!偏了!应该点旁边做题网址!”。可是蓝色的方型小电视的图标已经加载出来了,“此方的魔法正在控制我!”我心想。

等我再一抬头时,发现已经到了快要熄灯睡觉的时间了,只好匆匆赶回宿舍洗漱。至于什么动态规划,则是闻所未闻。

咒语的三

2 月 29 日,距离省选只有 2 天了,可是 lc 给我的动态规划题单却还只做了一半。复习时,我对着是 OI Wiki 上琳琅满目的知识点,一项一项耐心地枚举过去,可是却一点也看不出省选考哪个知识点我会有优势。

“省选总不能考 Alpha-Beta 剪枝吧”,我心想,几遍如此,我还是想着,“希望省选会有一道题,体现了我某个知识点的优势”。

flag 的二

3 月 1 日,这天广东大降温。一早起来床,窗外则是阴雨阵阵,北风呼啸。舍友进出时也不关门,一阵冷风灌进宿舍里,我感觉连被窝也不暖和了。我咬着牙从床上爬起来,把宿舍的门都关了。我心想明天都是省选了,今早林导总归不会再说什么,便又躺下去睡个回笼觉。这一下子就睡到快 9 点,我才爬起来把东西简单收拾了下,从宿舍保安的眼皮子底下潜行出了宿舍。

没想到一到机房,林导正在开会,而且已经接近尾声了。我急急忙忙地搬了个凳子,想偷偷从后面潜行进去,没想到后面已经人满为患了,只好做到了前面去。这一下,林导立即就发现了我,便把我当作“一些同学”讽刺了一通,然后便宣布散会。

出来之后,我问旁边的同学林导刚刚讲了什么。“无非是些老生常谈的东西”,旁边的同学摇摇头,仿佛在评判一个整日只会写“八股文”的老腐儒,“什么要测试好样例,要到 NOI Linux 的虚拟机里编译一下……”

“有没有什么新的奇妙论断,能和他之前的‘找不到女朋友是社交能力不行’之类的言论一样的‘金句’?”我打断道。得到否定的回答之后,我不满地想:“哼,也就会说这几句了,不知道还以为是 NPC 呢!什么编译之类的,呵,我在正式比赛中还从来都没编译错误过呢”。

当天下午 1 点,和计划一样,学校安排车把我们送到东莞中学松山湖实验学校报道。车上大家都默默无言,我打开电脑玩了一会 EndlessATC,再一抬头发现已经到了报到处。

flag 的三

在“GDOI 2024 与 GDTSC2024”的大横幅下面的就是报到处了,里面人很多。事实上,我们在广东省重点中学赛的时候就已经来过这所学校一次了。肥硕对这里的环境颇有微词:“这里就应该像 NOI 一样,给那个 SelfEval 程序……Windows太难用了”,虽然我平常也是一直使用 Linux 的,但是我却不以为然:“SelfEval 也没有特别好用吧,只要下发了大样例,怎么都好说”。

背景的四

虽然人很多,但是像二中这样的学校,在报到处拿牌子还是很快的。我们几乎是最早到达机房试机的学校。整个机房浑身上下都散发着一种厚重的历史感,近十年历史的旧电脑、从 2019 年就没有升级过的 Windows 10、以及最致命的——Dev-Cpp内自带的 GCC 4 是 Windows 里唯一的编译器。早在是 2010 年的 4 月 14 日,那是我在幼儿园牙牙学语的平凡的一天,这个编译器就发布了。

唯一还算先进的是 VirtualBox 里安装的虚拟机,可是,在这块 4 核 8G 内存的金属上,即使是强如甲骨文这样的公司,也难以在一个嵌套虚拟化环境中流畅的运行虚拟机。

至于外设,则更是一言难尽:拥挤的座位、摇摇晃晃的座椅,滚轮无法使用的鼠标。我立即反映了鼠标的问题,对方则是爱理不理地回答道:“我晚上看看”。

至于显示器,则是一台老旧的 VGA 屏幕,屏幕上的像素点清晰可见,更难受的是,屏幕上还有一条一条的纹路,明显是显示器部分烧坏的表现。可是我看了看和我同一个考场的 zxc 的屏幕,他的也是带着一条一条的纹路,“可能就应该是这样的吧”,我安慰自己。

我只好随便配置了一下 GVim,写了一个后缀自动机模版,通过了编译,甚至没有测试,就草草离场了。

回到报到处,教练让我等一下后面的同学,说是等会要在这里拍合照。在等待的过程中,我就和肥硕聊了几句,可他却说他的显示器没有一条一条的纹路,我大惊,原来是我和 zxc 的显示器正好有一样的故障。教练让我们立即回考场解决这个问题。

那考场设在了顶楼五楼,至于电梯,倒是有,只是电梯旁边还有一个人脸识别的机器,我一走过去,那机器仿佛我踩到了它的脚一般,大声尖叫起来:“无权限!无权限!”,也不允许我按电梯。等到我们俩气喘吁吁的爬楼梯跑到 5 楼,那里的人已经走光了。我们只好自己动手,从后面的备用机上拆了两个显示器下来换上,我还顺便把鼠标换了。

等到我们忙完回到报到处时,合照已经拍完了。

背景的五

在等待拍合照时,我还听肥硕说,似乎只要把文件放进回收站里,但不清空回收站,晚上就不会被考场的系统还原给删掉。我将信将疑,但是还是在回去换显示器的时候,顺手把刚刚写的后缀自动机板子放进了回收站。

背景的六

3 月 2 日,省选开始了!还是提前三分钟发了解压密码,这次密码似乎是完全随机的十六进制数,没看出什么含义。

开场先看第一题,当即判断它为简单题,立即简单得把 GVim 配置了一下就开始写,结果越写发现细节越多,而且发现有负数的情况不那么好处理。只好慢下来,先配置好环境,然后拿出草稿纸开始推式子,推了一会,发现就是按照绝对值的大小关系分类讨论,然后就是解几条不等式。

这时大概已经开场了 40 分钟,我把之前写的代码删得只剩输入部分,开始写 4 种情况的分类讨论。4 种情况的函数我直接复制粘贴了 4 份,这是很差的做法,后来发现哪里不对,又只好把 4 份全改一遍。更要命的是,这份质量极差的代码居然过了小样例,却没过大样例。我只好打开终端用 GDB 调试,搞了半天,发现,我们平常认为计算机里的整数除法是向下取整,例如:

assert(5 / 3 == 1);

但是这只对正数的情况成立,当有结果是负数的情况,就不那么对了,例如:

assert((-5) / 3 == -1);

添加判断改好了之后,又过了一个样例,可是最后一个样例还是过不去:有几个点答案认为无解,我的程序却认为找到了解。我用 GDB 进入到判断函数里面,把变量一个一个打印出来,发现我找到的解确实满足我所有的不等式!莫非是我最开始列的不等式本身就不对?我又到函数外面,再把变量一个一个打印出来判断了一次,发现找到的解突然又不满足不等式了——原来是我把判断函数的参数类型随时写成了 int,改成 long long 之后,果然通过了所有样例。

这时已经是快 11 点半了,里比赛结束只剩一个半小时,我急忙去看第二题,匆匆看了几眼我便断定它为困难题。再去看第三题,发现第三题也很困难!不过想了想,发现第三题只要输出 n! 就有 8 分,如果再加上基本的暴力的 16 分就可以拿到 24 分。对于这 24 分,我感到十分满意,不过我没有立即写,而是去回去想第二题的部分分怎么拿。

第二题看了半天,发现除了最基本的暴力我好像根本想不到任何其他部分分做法。第二题也只写最基本的暴力显然是不可接受的。不过我倒是设计了一个先二分,然后每次判断在 01 字典树上动态规划的,时间复杂度为 O(nk) 的做法,我感觉这个做法过于显然,以至于不敢相信它是对的。但是对于只剩下一多小时的我来说,只能死马当活马医了。我立即写了这个动态规划。

果然,第二个样例这个动态规划过不去了!我心下大惊,莫非是我的做法实际上是假的?眼见 NOI 冬令营时 3 小时调试一个假做法的惨剧几乎又要重演。我的内心十分纠结,到底是继续调试还是注释掉去写暴力?这时我想起还带了一块 lc 当时给我凤梨酥,便拿出来边吃边冷静一下。

时间只剩下四十多分钟了,我决定所谓“富贵险中求”,继续调试。没想到没过几分钟,我发现了一个导致变量混用的拼写错误,改过来后,竟然过了全部样例!我当时背后冷汗直冒,差点以为第二题是困难题而放弃了,没想到实际上第二题这么简单,还没有第一题困难。

不过,我在最后一个样例上要运行 8 秒多,这显然太慢了。我反复看了几遍代码,都觉得我的代码的时间复杂度十分正确,没有理由跑这么慢。“看来只是常数太大了”,我心想,“一定是 __int128 比较慢”。于是我便做了一点常数优化,好不容易把代码在最后一个样例上的运行时间压缩到了 4 秒左右。

还剩二十多分钟,我立即创建了第三题的代码,并且写了一个输出 n! 的 8 分的代码,中途以为式子有问题又停下来推了一会,花了大概十分钟才写完调通。显然,以我的代码能力,剩余的时间已经不支持我再去写那 16 分的暴力分了。心想 208 分已经不错了,我又检查了一遍文件读写,把每道题的程序又编译并测试了一遍样例。

最后几分钟,已经没什么事干了,我随手打开回收站,发现昨天写的后缀自动机的板子,果然还在里面。这样的组织能力,实在是贻笑大方。

出来之后遇到 zxc,他竟然认为第二题很难,遂给他讲了我的做法。他也认为我的做法很对,“可是这个做法的时间复杂度不是 O(nk2) 吗?”他问。我仔细一想,浑浑噩噩中一阵清醒:他说的是对的。根据我的做法,首先我需要二分,这一段的复杂度是 O(k),这是显然的。之后每次判断的动态规划,需要把字典树上的每个点都遍历一遍,而 01 字典树的节点个数不同于大部分二叉数据结构,并不是 O(n) 而是 O(nk) 的!这意味着我需要 O(1) 跳过字典树上的链总复杂度才是 O(nk) 的!而现在的 O(nk2) 只有 72 分。

想到仅仅是因为疏忽错误分析了复杂度,就丢掉了 28 分,再加上第三题没时间而没写的暴力的 16 分,一共就是 44 分了,我原本轻松愉快的心情一下子又变得沉重了起来。

第一天估分:100 + 72 + 8 = 180。

背景的七

时间来到 3 月 3 日,省选的第二场。今天的密码似乎依旧是完全随机的十六进制数,没看出什么含义。

吸取了昨天的教训,开场解压完题目,我先不慌不忙配置了环境变量,GVim 配置和输入法,然后才开始看题。这一次看完题,感觉第一题就很困难,而后面的两道题更是在我心中被预设了“不可做”的题目。不过想到昨天晚上 lc 和我说不会就写暴力。我就开始研究第一题的部分分。

很快我就发现了第一题用动态规划的一个做法:设 hi,k 表示节点 i 的子树内,选择到的第一个数的值一定大于等于 k 的最小代价。在 O(4n) 内计算 h 的递推式是十分显然的,而计算出 h 后也很容易就可以算出最终的排列,这样就有 n11 的部分分了,再加上性质 A 是一个容易的贪心,一下子就拿到了 80 分。

于是我觉高高兴兴地写了这个 80 分的做法,包括性质 A 的贪心,丝毫没有意识我的做法只要简单优化一下就能通过这题了。

等我写完调通,已经是 10 点半了,我开始看第二题。我对第二题完全没有思路,以至于 k0 的部分分完全没有思路。想着想着第二题,我忽然想起来第一题状态中,下标 k 的取值数量十分有限,只需把状态改为设 hi,k 表示节点 i 的子树内,选择到的第一个数的值一定大于等于该子树内第 k 小的数的最小代价,这样立即就优化为了 O(2nn),可以通过这题了。

这并不好写,等我写完这个改进,已经 11 点半多了。我连忙去写第二题的部分分,几乎是一个点一个点分类讨论,好不容易得了 20 分(其中还有一个情况讨论错了,所以我当时以为是 25 分)。

这时还剩下 40 分钟不到,我开始看第三题,忽然发现第三题考察的是超限序数,这个东西我在考前逛 B 站的时候正好看到过,以至于这题立即就变得简单了起来。我立即开始写,可是奈何终究还是时间不足,没调试出来。不过似乎有几个特殊性质的情况还是正确的。

出来听说肥硕过了第二题,十分震惊。不过又听说了好像许多人都甚至没创建第三题的文件夹,一想到我误以为第三题很难而没给第三题分配足够的时间,我的心情便立即由震惊变为的难过。

第二天估分:100 + 20 + 24 = 144。

背景的八

这一场比赛感觉和之前参加的省选很不一样。在之前的比赛中,虽然说是坐在电脑前写了四个半小时代码,但是由于会的太少,到后面虽然人还在看题目,心却已经无聊地开始扫雷了。这次却觉得明明有很多可以做的题,却“心有余而力不足”,因为时间分配不好或者细节考虑不周到,第一天丢了 44 分,第二天更是丢了 81 分。

背景的九

难道仅仅两天是丢了 125 分本来应该拿到的分数而已吗?如果真是这样,到还不错了,毕竟这样算起来两天加起来有 324 分,这个分数事后看来能排全省第 9 名,似乎足矣进入省队?

并非如此,不过在介绍原因之前,让我们来看看之前的 3 次许愿,或者说,3 条“咒语”:

  1. 希望今年的省选的第一天的第一题是一道细节很多的结论题,很多人都在这个结论的细节上犯错了,但是我的结论完全正确;
  2. 希望今年的省选每天都有能过的题;
  3. 希望省选会有一道题,体现了我某个知识点的优势。

每条咒语都应验了呢,第一天的第一题确实是一个细节很多的结论题;确实每天的第一题都很可做;以及,确实第二天的第三题应当体现了我的优势,即正好了解过超限序数,虽然我没把握住。

听起来这个情节像小说里的一样?仿佛在预示这一个光明的未来?但是也必须认识的我无意之间立下了 3 条致命的 flag:

  1. 只要我进了省队,一切就会好起来的;
  2. 我在正式比赛中还从来都没编译错误过呢;
  3. SelfEval 也没有特别好用吧,只要下发了大样例,怎么都好说。

呃呃,既然情节像小说一样展开,那么只要立下了“只要我进了省队,一切就会好起来的”的 flag,恐怕就进不了省队了;如果说出了“我在正式比赛中还从来都没编译错误过呢”,那么必然说话者就要马上就要遇到编译错误被打脸了;但凡评论说“SelfEval 也没有特别好用吧”这种话,那这集的末尾恐怕就要出现哭着喊“为什么不给我用 SelfEval!”的桥段了。

情节果然还是像小说一样展开了,第一天我在混乱中在第一题的代码里写下了:

inline check_aRlS(i64 R, i64 S, i64 &mn, i64 &mx) {
	return check_aRgS(-R, -S, mn, mx);
}

只要专门贴出这个代码片段,几乎任何人都可以立即看出来,这个函数漏写了返回值类型!可是在老旧的 GCC4 眼里,一个函数不写返回值类型就默认类型为 int,而我这里漏掉的类型是 bool,而默认的 int 也正好是兼容的类型,于是我在测试时没有遇到任何问题,也通过了所有样例。

更加阴差阳错的是,我平常 inline 都是单开一行的,例如

inline
int foo(int x, int y) {
	return x + y;
}

但是在这份代码的混乱地复制粘贴的过程中中,我不但漏掉了返回值类型,也漏掉了 inline 说明符后面的换行,导致 inline 占据了返回值类型的位置,在大量代码中,难以一眼看出来问题所在。如果我当时在 NOI Linux 下编译一下,就会立即得到这样的报错:

wind.cpp:45:8: error: ISO C++ forbids declaration of ‘check_aRlS’ with no type [-fpermissive]
   45 | inline check_aRlS(i64 R, i64 S, i64 &mn, i64 &mx) {
      |        ^~~~~~~~~~

(在 GCC4 上添加 -fno-permissive 选项可以让它也报错,但是我之前不知道)

加上 bool 这么 5 个字符之后,我的代码在洛谷上轻松通过了全部数据。因为这样的错误而一下子丢掉 100 分,只剩下 224 分的我,显然在广东这样的强省,已无缘省队。我回忆起咒语一:“希望今年的省选的第一天的第一题是一道细节很多结论题,很多人都在这个结论的细节上犯错了,但是我的结论完全正确”,为什么我当时不直接说我的代码在评测环境下通过了所有数据,而是选择了“我的结论完全正确”这样模棱两可的说法呢?不过话又说回来,谁许愿还像签合同一样严谨呢?

考虑到“保证代码能通过编译”显然不是选手的责任,否则主办方只要让选手在纸上写代码即可,也不必准备那么多电脑,因此显然只能说是我运气不如人,命里多磨难。

最终分数:NOIp 319,省选 224。

第一集的Ed

Cr_纯然 / 洛天依《最忧解》

日常的一

事到如今,我只能在浏览器收藏夹里删除 OJ 网址,而是转而拿出文化课的各种书了。至于下周回去上文化课之后又会发生什么有趣的事情,请见“Re:从零开始文化课日常的第二集”!

posted @   方而静  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示