少年辛苦终身事【第一篇】
本文由 ChatGPT 5.4 Thinking 编写。我仅仅提供每一章的故事、剧情梗概,进行个别文本的微调。不保证文章中的算法内容、或者每个题目的做法都是正确无误的。不建议使用本文学习算法或者当作题解阅读。
第一篇
第一章:第一行代码
那年秋天,济南的风来得很早。
九月刚过,天就已经有了凉意。傍晚放学的时候,校门口总挤着一层层接孩子的家长,电动车的铃声、自行车的刹车声、路边烤地瓜的甜香、校服摩擦的窸窣声,混在一起,像一锅热腾腾又有点吵闹的生活。陈紫昂背着书包,个子不算高,头发剪得很短,额前总有几缕不太服帖,跑起来时会一颠一颠地晃。
他那时候三年级,数学还算不错,做题快,脑子也活。老师在课堂上出一些稍微拐个弯的小题,他往往是最先举手的那一个。可在这个时代,这样的“不错”,其实并不算什么。
在这个省城,在这个所有家长都隐约知道“信息学竞赛能改变命运”的年份里,太多孩子从一年级、甚至幼儿园大班就开始学算法了。有人能把一整面墙的奖状贴满,有人每周末辗转于各个强校训练营之间,有人已经会写出让大人都看不懂的长长代码。三年级的家长群里,早就不再讨论“奥数要不要学”,而是在悄悄比较:你家学没学 OI?学到哪了?拿过什么评级?
陈紫昂第一次听见“OI”这个词,是在一次很普通的家长会之后。
那天妈妈从教室里出来,神情比平常更认真。她一边替他整理衣领,一边问:“紫昂,你想不想学一种……比数学更像搭积木的东西?”
陈紫昂抬起头,眼睛亮了一下:“积木?”
“不是那种积木,是电脑上的。”妈妈想了想,尽量把这件事说得温和些,“就是给电脑下命令,让它按你的想法做事情。很多特别厉害的小朋友都在学,叫信息学竞赛。”
他对“竞赛”没什么概念,却对“给电脑下命令”有一种天然的好奇。
家里的电脑在书房,黑色的显示器,开机时会发出轻微的嗡鸣。陈紫昂很喜欢看那个亮起来的屏幕,觉得里面像藏着另一个世界。可他以前只会在上面玩一些很简单的小游戏,或者看动画片。至于“让电脑听懂自己的话”,那几乎像魔法一样。
“我学了以后,电脑就会听我的吗?”他问。
妈妈笑了一下:“听一部分。前提是你说得足够清楚。”
这句话后来他记了很久。
那周周末,妈妈带他去了机构试听。
机构在一栋写字楼的七层。电梯门一开,走廊里很安静,只有尽头那间教室透出白色冷光。玻璃门上贴着几行蓝色的字:青少年信息学思维课程。门旁边还有一块电子屏,滚动播放着往届学员的成绩:省赛一等奖、NOIP 入围、NOI 省队。那些字对三年级的陈紫昂来说太遥远了,像天上的星星,知道很亮,却根本摸不着。
他跟着妈妈走进去的时候,教室里已经坐了七八个孩子。有人比他大,看着像四五年级;也有两个个子和他差不多的小孩,正盯着屏幕敲键盘,手速快得像在弹琴。教室里有一种很特别的气味,混着空调冷风、塑料键盘和刚擦过白板的淡淡酒精味。
讲台上站着的是位年轻老师,姓周,瘦高,戴眼镜,说话不快,却很清楚。他没有一上来就讲那些听起来很吓人的名词,而是在白板上画了一个小机器人。
“同学们,”他说,“你们觉得,电脑聪明吗?”
有人说聪明,有人说不聪明。
小周老师笑了笑,在机器人旁边写下两个大字:精确。
“电脑不是聪明,它只是非常非常听话。你告诉它做什么,它就做什么;但如果你没说清楚,它就会严格按照你没说清楚的方式做错。”
他拿起白板笔,写了第一行代码:
cout << "Hello, world!";
屏幕上立刻跳出一行白字。教室里传来一阵细小的惊呼。陈紫昂坐在第三排,眼睛一下子睁圆了。
原来真的可以。
不是动画片里那种夸张的魔法,而是一种更安静、更干净的力量。你敲下几个看上去古怪的符号,屏幕就照你的意思回应。那一瞬间,他觉得自己好像站在一扇门前,门后是一个巨大的、规则森严却又闪闪发亮的世界。
试听课结束以后,妈妈问他要不要学。
陈紫昂没有立刻回答。他回头看了看教室里还亮着的屏幕,几个先来学的孩子正在做题,界面里密密麻麻全是英文和符号,看上去像一片没有路标的森林。他有点怕,又有点舍不得离开。
“难吗?”他小声问。
“难。”妈妈很诚实。
“那我能学会吗?”
妈妈看着他,停了一下,说:“别人能不能学会我不知道,但你只要肯学,总会学会一点。至于能学到多远,得一点一点走。”
陈紫昂点了点头。
他那时候还不知道,在这个世界里,“一点一点走”也是一件很奢侈的事。因为前面有太多人,跑得太快了。
从那以后,他的生活开始有了新的刻度。
每周两次课,一次在周三晚上,一次在周六下午,加起来五个小时。其余时间,他自己再练十个小时。对一个三年级孩子来说,这已经不是“顺便学学”,而是认真地把一块生活切出来,专门献给某件事了。
最开始的两个月,学的是最基础的语法。
输入输出、顺序结构、分支、循环、数组、函数、结构体、字符串。
这些词在大人耳朵里也许只是课程大纲,可在陈紫昂的世界里,每一个词都像一块新摸到的石头,冰凉、坚硬,不知道里面有没有火。
第一节正式课,小周老师让他们每个人打开 Dev-C++,先写最简单的输入输出。
“输入两个数,输出它们的和。”
陈紫昂盯着键盘,手指悬在半空,像在犹豫要不要跳进一条河。他刚学会敲英文括号,分号总忘记加,int main() 后面的花括号也时常配不对。旁边一个四年级男孩已经写完了,开始伸懒腰,而他的屏幕上还停着红色报错。
那行报错他根本看不懂,只觉得像电脑在生气。
“老师,我哪里错了?”他举手,声音不大。
小周老师走过来,弯下腰,看了一眼,笑了:“这里少了一个分号。”
“就因为这个?”
“对,就因为这个。”小周老师拿起鼠标,在那一行后面轻轻一点,“你看,电脑就是这样。你差一颗米,它都不替你补。”
教室里响起几声轻笑。陈紫昂耳朵有点红,却也把这句话记住了。
电脑不懂“差不多”。
后来很长一段时间,他都在和这种“不懂差不多”的世界磨合。
学顺序结构时,他觉得代码像搭小火车,一节一节连起来,前面走错,后面全歪;学 if 分支时,他又觉得像站在路口——如果下雨就打伞,否则就跑回家;学循环时最有意思,for 和 while 像两个不知疲倦的小士兵,可以帮你把一件事做一百次、一千次,只要你命令得足够准确。
他第一次真正感到“代码有力量”,是在学循环的那个晚上。
题目很简单:输出 1 到 100。
如果一个一个写,当然也可以。可当小周老师敲下那几行短短的代码,数字像水一样整整齐齐流满屏幕时,陈紫昂心里“咚”地跳了一下。他忽然明白,编程不是让人辛苦去做重复的事情,而是让人找到一种办法,让机器替你把重复扛走。
这感觉让他着迷。
回家的路上,夜已经很深,济南初秋的风吹在脸上发凉。他坐在妈妈电动车后座,双手揪着妈妈外套的边角,脑子里却还在想循环。
“妈妈,”他在风里喊,“如果我要输出一万次,是不是也能一下子弄出来?”
“能啊。”妈妈大声回他。
“一亿次呢?”
“理论上也能。”
陈紫昂沉默了几秒,忽然笑了起来,笑得肩膀一抖一抖的。那是一种很孩子气、很纯粹的快乐,好像他刚刚捡到了一件只有自己知道厉害的宝贝。
当然,不是所有内容都这么友好。
数组出来的时候,他第一次有了明显的挫败感。
小周老师在白板上画了一排小格子,说,数组就像一条一条排好的抽屉,每个抽屉都有编号,可以往里面放东西,也可以按编号把东西拿出来。陈紫昂一开始觉得很好懂,可真到写题的时候就乱了:下标从 0 开始还是从 1 开始?为什么 a[10] 有时候能用,有时候又会出问题?为什么他明明输入了五个数,最后打印出来的却只有四个?
那周三晚上,他在机构里做一道最基础的数组题,做了快四十分钟。屏幕上的代码改了又改,输出结果还是不对。教室里的空调呼呼吹着,他的手心却有些发热。前排有人已经开始做下一题了,键盘声又密又快,像雨点一样打在桌面上。他盯着自己的屏幕,忽然生出一种很清楚的感觉——
自己好像真的起步晚了。
这种感觉并不宏大,也不戏剧化。它只是很轻地落在心里,像一小片雪。别人写数组像在翻自己家抽屉,他却还在数抽屉有几层;别人已经习惯了报错,他还会因为一行红字而心慌。
那天回家以后,他没有像平时那样先去喝水,而是直接坐到书桌前,把老师课上讲的数组笔记又抄了一遍。方格本摊开,他握着铅笔,一个一个小格子画得很认真,从 a[0] 画到 a[9],在每个格子下面写编号。
妈妈端着切好的苹果过来,看见他趴在那里,鼻尖几乎要碰到本子。
“累不累?”妈妈问。
陈紫昂摇摇头,过了一会儿才闷闷地说:“妈妈,他们好多都学过了。”
妈妈没接“他们”,只问:“那你今天学会一点没有?”
他想了想:“……学会一点。”
“那就行。”妈妈把苹果盘轻轻放下,“别人比你早,不代表你今天这一步白走了。”
陈紫昂没有立刻回答。他低下头,又把那排数组格子描深了一点。窗外是楼下广场舞的音乐声,隔着玻璃听上去有点远。书房里只有台灯暖黄色的光,照着他的睫毛,也照着那一页很笨拙却很认真的笔记。
那一晚,他第一次隐约明白:OI 不是那种学一节课就会立刻有回报的东西。它更像在黑暗里慢慢摸一条路。你知道前面有山,有很多比你走得快的人,但你还是得低头看清自己脚下这一小块地,踩稳,再迈下一步。
再往后,函数、结构体、字符串一个接一个地来了。
函数对他来说像“给动作起名字”。把一连串复杂的步骤装进一个小盒子,需要的时候就叫一声它的名字,它就会出来帮忙。这让他觉得很神奇,像收服了一只看不见的小兽。结构体则更抽象一些,小周老师说,它能把同一个东西的不同信息放在一起,比如一个学生,有姓名、分数、年龄。陈紫昂听得半懂不懂,但又觉得这很像整理书包——铅笔、橡皮、尺子,不要散着放,要放进同一个文具盒里。
最让他头疼的是字符串。
那些双引号里的字符像一群调皮的小鱼,一会儿要比较大小,一会儿要逐个处理,一会儿又要注意结尾的 \0。小周老师讲到这里的时候,班上不少孩子都露出了迷茫的表情。陈紫昂咬着笔帽,看着投影上的代码,觉得自己的脑子像刚跑完步一样发热。
但他也在慢慢变。
最开始,他写错了就会发呆,看着屏幕像看天书;后来他会自己先检查括号、分号、变量名;再后来,他甚至会学着像老师那样,把程序从头到尾“在脑子里跑一遍”,想象每一步变量会变成什么。
这是前两个月里最不容易被看见、却最重要的变化。
他不是一下子变得厉害了,而是开始懂得怎么和代码相处了。
周六的课后,机构常常会留半小时自习。窗外天色渐暗,楼群在玻璃外一层层亮起灯。教室里只剩下键盘敲击声和偶尔的翻书声。陈紫昂坐在靠窗的位置,小脸被屏幕映得发白,神情却格外专注。他有时会被一道最基础的输入输出题卡住三分钟,也会因为终于把一个循环写对而在心里偷偷高兴很久。
那种高兴很小,别人看不见,连他自己都说不清楚。但它像火柴,一根一根地擦亮,慢慢把最初的陌生和害怕烧掉了一点。
两个月过去的时候,小周老师在课上做了一次小测。
题目都不难,考语法和最基础的程序设计。陈紫昂做得不算特别快,中间还在字符串那题上犹豫了一会儿,可最后交卷时,他忽然有一种很奇怪的感觉——第一次来试听课时,屏幕上的那些符号像森林;而现在,他虽然还走不远,却已经能辨认出几条路了。
下课时,小周老师把卷子发下来,在他的名字旁边写了一个分数,算不上拔尖,却也挺稳。
“基础还可以。”小周老师看着他说,“你起步晚一点,但理解能力不错。接下来别急,先把这些东西练熟。”
“我能追上吗?”陈紫昂脱口而出。
这句话问得很轻,像是怕被别人听见。
小周老师愣了一下,似乎没想到一个三年级孩子会这么直白。他没有说那些好听却空泛的话,只是看了看他,认真地说:
“能不能追上很难说,因为前面的人也在跑。但你要先跑起来。OI 这个东西,最怕的不是慢,是站着不动。”
那天晚上,陈紫昂背着书包走出写字楼,楼下的风比来时更凉了一点。妈妈牵着他的手,问他今天怎么样。
他想了想,说:“有点难。”
“然后呢?”
“……但我想继续学。”
他说这句话的时候,声音不大,却很稳。
夜色里,城市的灯一盏一盏亮着,像无数遥远又冷静的眼睛。这个世界对信息学的孩子们从不温柔,赛道又长,前面的人影密密麻麻,像山路上一串看不到头的灯火。陈紫昂站在最初的入口处,鞋带还没系得特别紧,脚步也算不上快,可他终于已经把手伸进了那片风里。
门开了一条缝。
他还很小,刚刚学会输入和输出,学会用循环让数字排队,学会在数组的小格子里放进自己的理解,学会在报错面前不立刻慌张。那些进步都很轻,很薄,很不起眼,像秋天早晨落在窗台上的第一层霜。
可真正漫长的路,往往就是从这样一层薄霜开始的。
第二章:洛谷上的灯
十一月过半,济南的天越来越短了。
下午五点多,学校门口就已经有了冬天的颜色。风从操场那边灌过来,卷着细碎的灰尘和枯黄的叶子,吹在人脸上有一点疼。陈紫昂把校服拉链一直拉到下巴,背着书包快步往外走,脸蛋被风吹得发红,鼻尖也有点凉。
妈妈在路边等他,车篮里放着一个保温杯,里面是温温的水。
“今天晚上有课,快一点。”妈妈说。
陈紫昂点点头,坐上后座。电动车往前一窜,晚高峰的灯就一点一点在他眼前拉开。路边商铺亮起招牌,写字楼的窗户像一格一格发白的小方块,城市在天色里显得很冷,可也很亮。那是他这两个月最熟悉的傍晚:学校、吃饭、去机构、坐在电脑前,再回家,回家以后还要写一会儿题。
十五个小时一周。
对于大人来说,这也许只是一个数字。可落在一个三年级孩子的生活里,它就像在原本已经排满的课桌上,又放上了一摞厚厚的新书。周三和周六去机构,两次课一共五个小时;其余十个小时,要从晚饭后、周末上午、周日下午一点一点抠出来。别的孩子放学以后踢球、看动画片、骑自行车,他也不是完全不羡慕,只是每次羡慕过后,还是会默默把书包往桌上一放,把电脑打开。
因为他知道自己起步晚。
这句话从来没人真正拿来责怪过他,却像一颗小钉子,轻轻钉在他心里。平时不太痛,可每当他看见那些比自己小一点、却已经写得飞快的孩子,或者在机构里听见别人谈论“省选模拟”“提高组题单”“某强校冬令营讲义”的时候,那颗钉子就会往里轻轻陷一点。
也是在这样的一个晚上,小周老师把“洛谷”这个名字,第一次正式带进了他的世界。
那节课刚讲完模拟。白板上还留着几行板书,投影屏幕映着浅蓝色的光。小周老师合上笔记本,没急着下课,而是转过身问大家:
“你们回家练题,平时都在哪做?”
前排几个大一点的孩子立刻报了几个平台名字。有人说自己已经刷到普及/提高,有人说在跟学校的题单。陈紫昂安安静静地坐着,没说话。他平时练的,大部分还是老师发的讲义和课堂作业,题目一页一页打印出来,边角被翻得有点毛。
小周老师目光扫过来,在他身上停了一下。
“紫昂,”他叫他名字,“你回去让妈妈帮你注册一个洛谷账号吧。”
陈紫昂抬起头。
“洛谷是题库平台,”小周老师说,“题很多,也分层。你现在起步晚了一点,但理解能力不错,不能只靠每周这两次课。平时自己多做题,把基础打厚一点,能追一些回来。”
“追一些回来。”
这几个字不重,却一下子敲进了陈紫昂心里。
下课以后,他一路都在想那个名字。洛谷。听起来像一条很深很长的山谷,里面藏着很多题,也藏着很多比他厉害的人。
回到家,妈妈刚把饭端上桌,他就迫不及待地说:“老师让我注册洛谷。”
“什么谷?”妈妈正在盛汤,一下没听清。
“洛谷。做题的网站,中国最大的信息学竞赛题库平台。”他说这句话时,语气里有一点努力装出来的熟练,像是想让自己也显得已经迈进了那个世界。
妈妈把手机拿出来,搜了一下,很快找到了注册页面。书房的灯暖暖地亮着,电脑屏幕反着光,陈紫昂坐在椅子上,背挺得很直,像在办一件郑重其事的大事。
“用户名想叫什么?”妈妈问。
陈紫昂愣住了。
这问题比他想象中难。他盯着屏幕,想了半天,脑子里掠过很多稚气的名字,又都觉得不太好。最后他小声说:“就叫……ZiAng 吧。”
妈妈笑了笑,帮他敲进去。
按下注册按钮的时候,屏幕跳转,一个崭新的个人主页弹了出来。那一刻,他心里生出一种很奇妙的感觉,像是在某座巨大无比的城市门口领到了一张自己的小小通行证。这里面有成千上万道题,有无数比他强的人,也有未来可能属于他的、一个个绿色的通过标记。
那天晚上,他做了洛谷上的第一道题。
很简单,几乎和课堂上的输入输出没什么区别。可是平台的界面和纸上的作业完全不同:题目标题、难度标签、提交按钮、评测结果,全都整整齐齐地排在那里,冷静又明亮。陈紫昂读完题,深吸了一口气,把代码敲进去,手指因为紧张有些僵。
点击提交。
屏幕上转出一个小圈,像有人在门后审判他的答案。
几秒之后,结果出来了——Accepted。
绿色。
很小的一块绿色,却亮得惊人。
陈紫昂先是愣了一下,随即眼睛一点一点睁圆,几乎整个人都往屏幕前凑过去。他虽然早知道自己大概做对了,可那种“被系统正式承认做对”的感觉,和老师说一句“嗯,差不多”完全不一样。那是一种干净利落的确认,没有情面,没有鼓励,就是对,或者不对。
“妈妈!”他回头喊,“绿了!”
妈妈正在门口叠衣服,没太听懂:“什么绿了?”
“题!我过了!”
他声音里有一点控制不住的兴奋,像冬天夜里突然擦亮了一根火柴。
从那以后,洛谷成了他生活里一盏固定亮着的灯。
中间这两个月,课上的内容开始真正往“算法”里走了。
模拟、枚举、排序、递推。
这些词和前两个月的语法不一样。语法像是在学字母、学发音,虽然枯燥,但边界很明确;而算法更像是开始学“怎么想”。老师讲的不再只是“这句代码是什么意思”,而是“为什么应该这样做”“如果你不知道答案,就先把过程演出来”“当问题太复杂时,能不能把所有可能一种一种试过去”。
陈紫昂最先接触的是模拟。
小周老师说,所谓模拟,很多时候并不神秘,就是老老实实按题目说的过程去做,把现实里的规则搬到程序里,让电脑替你一遍遍执行。听起来简单,可真做起来却非常考验耐心。题目描述往往很长,像一串缠在一起的毛线:走一步,再转向;减一点,再判断;相同的时候这样,不同的时候那样。只要漏看一条,整个程序就会跑偏。
陈紫昂起初很不适应。
他太容易着急了。看见题目长,就想赶紧下手;看懂一半,就觉得自己全懂了。结果代码写出来,样例能过,提交上去却是一片刺眼的红。
Wrong Answer。
有一回他做一道很典型的模拟题,题面里有几个很细的条件:相等时不交换,轮到末尾时重新回头,某种状态下要多停留一次。他看得似懂非懂,凭感觉就写了。第一次提交,WA。第二次,还是 WA。第三次,他坐在桌前,把嘴唇都抿紧了,重新读题,才发现自己漏掉了一句几乎藏在段落中间的话。
他那天晚上闷了很久。
妈妈把水果放到桌边,他也只是“嗯”了一声,眼睛没离开屏幕。书房里很安静,窗户外面偶尔传来楼下车驶过的声音。台灯照着桌上的草稿纸,上面已经被他画满了箭头、方框和小人的移动路线,像一张拙笨但认真的作战图。
他对着那张草稿纸,一步一步手推流程。
往前走,停,转弯,判断,再走。
推到后面的时候,他忽然发现,原来不是自己不会写,而是自己没有真的看懂题。他太想快了,快到以为“差不多懂了”就等于“懂了”。
第二天上课,小周老师看了他的代码,没有急着讲技巧,只说了一句:“模拟题最怕心急。题目说什么,你就老老实实当一个翻译,不要替题目自作主张。”
陈紫昂点点头,把这句话记在本子角上。
后来他做模拟题时,真的慢下来了。先看,画图,再写;写完不急着交,而是拿样例自己跑一遍。他还是会错,但错得越来越有章法。那种一点点把题目“翻译”成程序的过程,让他慢慢生出一种以前没有的踏实感——不是撞对了,而是我知道自己为什么这样写。
枚举给他的感觉则完全不同。
如果说模拟像翻译,枚举就像在一片树林里一棵一棵地搜。题目问你哪一种可能成立,你不知道最聪明的办法,就先把所有可能都试一遍。小周老师在白板上画了很多小格子,说这就是“穷举”,笨一点,但可靠;只要范围不大,试完了,总能找到答案。
陈紫昂很喜欢这种思路。
它给人一种很朴素的安全感:我也许不够聪明,一眼看不出来最优解,但我可以不偷懒,把每一种可能都认真走完。对一个刚起步的孩子来说,这几乎像一条专门为他留出来的小路。
他开始做许多带着枚举味道的题。
枚举时间、枚举数字、枚举位置、枚举答案。
有些题从表面看很唬人,题面长长一串,他一开始读得心里发虚;可拆开以后发现,无非就是几个循环套在一起,一个一个去试。每当这时候,他心里都会升起一种小小的、安静的快乐:原来复杂的题,也能被拆成这么老实的样子。
当然,也不是所有题都这么温柔。
三个循环套在一起的时候,他还能忍;四层一套,他就开始晕。变量名在脑子里打架,范围一大,他就怕超时,怕自己写得又慢又乱。有一回他在洛谷上刷到一道普及-的枚举题,提交了七次,状态栏上整整齐齐排着一串红色记录,像一排小小的羞耻。
他盯着那几行记录,觉得脸都有点热。
洛谷这种地方,很容易让人看见自己的笨拙。你提交,系统就记下;你错了,页面也不替你遮掩。做题的人太多了,排行榜、讨论区、题解区,到处都是比你更快、更明白、更漂亮的答案。陈紫昂有时会偷偷点开别人的题解,看到那些清爽利落的思路,再回头看自己那段歪歪扭扭、变量乱飞的代码,会突然觉得心里空一下。
自己好像真的是慢。
冬天就是在这样的慢里一点点深起来的。
排序是他第一个真正觉得“代码有点帅”的部分。
小周老师讲冒泡排序那天,白板上画了许多个高高低低的小柱子。老师说,两个两个比较,大的往后走,小的往前留,一轮一轮,像气泡慢慢往水面上浮,所以叫冒泡。他说这话时,语气很平,陈紫昂却一下子记住了。
那天回家以后,他自己在草稿纸上画了八个数字,画成一列小方块,然后模拟它们交换位置。3 和 5 比,5 往后;2 和 8 比,8 往后。数字们在纸上挪来挪去,像一群排队调座位的小孩,笨拙,但最后真的慢慢整齐起来。
他觉得很神奇。
原来“从乱到有序”这件事,也能被写成程序。不是凭感觉,不是靠眼睛一看就知道,而是靠一套清清楚楚的规则,重复执行,直到所有混乱都归位。
后来学到选择排序、再到老师提一嘴更快一点的排序思想时,陈紫昂的心里第一次隐约有了“算法不是只有做题,它本身也很好看”的感觉。那种美并不热闹,也不张扬,而是一种很冷静的秩序感:你找到了规律,世界就愿意按规律给你让路。
递推则更像一扇安静但深一点的门。
小周老师讲递推那天,窗外在下小雪。雪不大,细细地落在窗玻璃上,很快就化了。教室里暖气开得足,孩子们的脸都红扑扑的,偶尔有人打个哈欠。小周老师在白板上写了一列数,问大家:前面知道了,后面能不能从前面推出来?
“递推的意思,就是你不一定直接知道第十步是什么,但你知道第一步、第二步,也知道从前一步怎么走到后一步。那你就可以一点一点推过去。”
陈紫昂听得很认真。
那一瞬间,他忽然觉得,这个词不只是在讲题,好像也在讲他自己。前面会什么,后面就能再推一点;今天会一点,明天就再多一点。不需要一下子看到很远,只要知道下一步怎么走。
但真正做题时,递推远没有这个比喻这么温柔。
状态怎么定义,初始值是什么,转移时漏没漏情况,稍微不稳就会错得一塌糊涂。有一次他做一个台阶问题,明明上课讲过类似的,自己一写却写得乱七八糟。f[1] 到底是多少?f[2] 算几种?如果题目条件稍微变一点,还能不能照搬?他在书桌前拧着眉头,铅笔在草稿纸上写了一排又一排小数字,到最后纸上全是橡皮擦过的灰印。
他有些烦。
不是那种想摔笔的大烦,而是一种小孩子特有的、委屈似的烦:为什么明明听的时候觉得懂了,自己一做就又不会了?
那晚妈妈端牛奶进来时,看到他耷拉着脑袋,额头都快碰到桌面了。
“卡住了?”妈妈轻声问。
“嗯。”
“老师讲过的?”
“讲过。”
妈妈沉默了一会儿,没有说“你再想想”,也没有立刻劝他休息,只是把牛奶放下,说:“那就先别急着做题目,先想清楚第一步。不会的人,最容易一着急就直接看最后一步。”
陈紫昂没吭声。
过了一会儿,他真的把题目放到一边,重新拿了一张纸,只写第一步、第二步、第三步。一个一个列过去,写着写着,后面的数竟然真慢慢顺出来了。
他看着那串终于连起来的数字,忽然有点想笑。
那道题最后他过了,虽然不算快,可通过之后的那一瞬间,他心里的高兴和做第一道输入输出题时又不一样。那次是新鲜;这次则像自己把一块卡在胸口的石头一点点搬开,终于看见后面亮着的路。
接下来的日子,洛谷开始一点一点记录他的成长。
他做题,交题,错,改,再交。
Accepted 一道,主页上的数字就涨一点。红色的 WA、紫黑色的 TLE、黄色的 CE 也还是有,可绿色慢慢多起来,像冬夜里越攒越多的小灯。
入门题最先刷开。
输入输出、分支、循环、简单数组、简单模拟、基础数学,最开始还会时不时卡住,后来渐渐熟了。他做题的速度没有突飞猛进,可稳定得多。以前一道题常常要在屏幕前坐很久,心里发慌;现在他会先读题,再分类:这是模拟,还是枚举?用循环就够了吗?要不要排序?能不能递推?虽然判断得并不总对,但他的脑子里开始出现“方法”的轮廓了。
这件事很重要。
从“我会写代码”,到“我知道这道题大概该往哪边想”,中间看起来只隔一层纸,其实隔着一整段入门者最黑的夜路。
有一段时间,他几乎每天都在洛谷上做题。
周三上完课回来做,周四放学后做,周五吃完饭再做一会儿;周末机构上完课,回家还要补两个小时。冬天的晚上来得早,书房窗外常常是一片沉沉的黑,玻璃上蒙着雾气。台灯下面,他裹着一件家居服坐在椅子上,手指小小的,敲键盘却越来越熟了。桌角放着保温杯,里面的水凉了又续;草稿纸一张接一张写满,边上堆着用过的铅笔屑和橡皮渣。
有时候他做得顺,半小时过三四题,心情就会格外好,去洗漱时走路都有点轻;有时候一晚上卡一题,卡到最后都快睡觉了,页面上还是红的,他就会闷闷不乐,躺在床上都在脑子里想循环和数组。
妈妈偶尔会劝他休息一下:“别把自己绷太紧。”
他嘴上答应,过十分钟又悄悄跑回书房,说还想再试一次。
这股劲,连他自己都说不太清是从哪来的。
也许是因为洛谷上那一串串通过记录真的很诱人。每多一道题,就好像离某个目标近了一点;也许是因为春天的山东省三年级小学生排名赛越来越近了,机构里、大群里、家长之间,都开始反复提起这个名字。那不是一个随便参加的活动,而是一场会被当地知名初中认真关注的比赛。对于很多家庭来说,它像一个很早很早的信号:这个孩子,值不值得继续往这条路上压更多的时间、资源和期待。
陈紫昂虽然还小,却不是完全不明白这些。
他听不懂所有的大人话,却知道“排名赛很重要”“好学校会看”“很多孩子从很早就开始准备”。他还知道,自己在这条赛道上不是最早出发的人。别人已经在前面跑了一段,他现在只能低着头,把能补的路一点一点补上。
有一次周六下课,机构里几个家长站在走廊上聊天,说起某个孩子二年级就把入门和普及-刷得差不多了,现在已经在碰普及/提高-。陈紫昂背着书包从旁边经过,脚步不由自主慢了一下。
那一瞬间,他心里又有一点发紧。
晚上回家,他没有像平时一样先做当天布置的课堂题,而是直接打开洛谷,把老师给他的题单往下翻,一题一题做。那天他的状态其实不算好,前两题都错了一次,第三题也做得慢。可他没有闹,也没有说累,只是咬着牙改。
妈妈后来回想,那天他在书桌前坐着的背影,第一次有了点不像三年级小孩的沉。
十二月底的时候,小周老师给每个孩子看了一次阶段进度。
教室里开着暖风,玻璃外天灰蒙蒙的。小周老师把几个人最近的练习情况和平台记录列出来,一个一个点评。轮到陈紫昂时,老师看着他,点了点头。
“入门题过得挺扎实,普及- 也开始稳定了。虽然起步晚,但你肯做,理解也还行。接下来别乱跳难度,先把基础算法再磨一磨,春季排名赛有机会冲到一个还不错的位置。”
“还不错的位置。”
这句话让陈紫昂心口轻轻一热。
不是“很厉害”,不是“肯定能赢”,只是“还不错”。可对那时的他来说,这已经是一种很珍贵的允许——说明他并不是完全追不上。
之后那几个星期,他练得更紧了。
他把洛谷当成自己的第二课堂。做题之前会先在本子上写日期,写今天要做几道入门、几道普及-;做完后就在旁边打勾。题目越来越多,打勾的痕迹也越来越密,像一片片很小但很固执的脚印。
入门 150 题,普及- 150 题。
对于那些从幼儿园起就在这条路上训练的孩子来说,这个数字也许算不上夸张;可对于一个三年级、零基础起步、才学了四个月的男孩来说,这已经是很多很多个夜晚了。是无数次点开题面、读题、发愣、写错、重来;是手心发热地等评测结果;是 Accepted 时偷偷抿住嘴角,不让自己笑得太明显;也是 WA 时鼻子发酸,却还是坐回去继续改。
他并没有因此一下子变成机构里最耀眼的孩子。
前面还是有人更快,学得更早,刷题更多。有人已经能把普及/提高-的题讲得头头是道,有人提交记录一页绿得像草坪,有人参加线上赛时的分数高得让人不敢直视。陈紫昂也会羡慕,也会在夜里短暂地怀疑自己:是不是开始得太晚了,是不是怎么追都只能追到别人身后。
但慢慢地,他也开始有了属于自己的小小证明。
比如有些题,他已经能在老师讲之前自己写出正解;比如做新题时,他不再只是茫然地盯着,而会下意识去想:先模拟?还是枚举?要不要排序?能不能递推?再比如机构里的小测,他的分数开始稳稳地落在中上,不算惊艳,却也不再是刚来时那个总为分号和下标发愁的小孩了。
这些变化都不大,却很真实。
冬天最冷的那几天,济南的清晨会起一层白霜。陈紫昂去上学前,常常能看到小区花坛边薄薄的一层冰,太阳还没出来,天色是发灰的蓝。他背着书包走过去,呼出的气在冷空气里化成一团白雾,很快又散掉。
有一天早上,他走到单元门口,忽然回头看了一眼楼上的窗户。书房那扇窗还留着昨夜灯光照过的痕迹,玻璃内侧一小块有些模糊,是他前一晚坐在那里写题时哈出来的雾气。
他站在冷风里,忽然想到,自己这两个月像是在一点一点往那扇窗后面挪。外面的人看过去,也许只觉得一个小孩在学编程,刷题,准备比赛;只有他自己知道,那些题不仅是题,还是一条很窄很长的桥。他在桥上,走得不快,甚至偶尔踉跄,可毕竟已经走了这么多步。
元旦过后,离春季排名赛更近了。
机构里关于比赛的话题明显多起来。谁家孩子报名了,哪所学校会关注,往年二等奖大概在什么水平,今年题目会偏模拟还是偏枚举,家长们在走廊、在群里、在接送路上反复谈论。那些名字——省实验、山大附中、历城二中体系里的初中、各类知名学校的少年班——像一个个高高悬着的目标,漂浮在所有孩子头顶。
陈紫昂不完全懂这些学校意味着什么,可他能感觉到空气里的紧。
小周老师也更认真了。讲题时会反复强调读题、细节、基础算法的熟练度,偶尔还会在课后单独提醒他:“你别急着和最前面那批孩子比,先把该拿的分拿稳。三年级组的题,基础很重要,越基础越不能掉。”
“该拿的分拿稳。”
这是一句很朴素的话,但落到这个年代的信息学里,几乎就是某种生存法则。
陈紫昂听进去了。
他开始有意识地回头复习自己做过的题。做错过的,重新做;做得慢的,再来一次;看着眼熟却说不清思路的,也要再过一遍。他渐渐明白,刷题不是把数字堆上去那么简单。有些题你“做过”,不等于你“会了”;有些通过,只是恰好这次没错;真正的掌握,是下次再遇到时,你心里会发出一声很轻的“哦,是这个”。
那个“哦”,他越来越常听见。
一月中旬,小周老师统计了一下他的平台进度。入门一百五十题,普及- 一百五十题,清清楚楚摆在那儿。数字并不夸张,却也绝不算少。老师把本子合上,对他说:“挺好了。至少说明这两个月你没白练。”
陈紫昂低头看着自己的鞋尖,心里有一点热,又有一点酸。
因为他知道,这句“挺好了”的背后,其实还有一句没说出来的话:可前面的人还是很多,你还要继续跑。
但那天回家的路上,他心情还是很好。
济南的夜风很冷,路边树枝光秃秃的,街道两边挂着新年的灯饰,远远看去有些温暖的颜色。妈妈骑着车,他坐在后面,手缩在袖子里,脑子里却在一遍一遍想自己的题数:一百五十,一百五十。
那不是奖状,不是名次,也不是谁能看见的荣耀。
可那是他自己一题一题做出来的。
每一道题后面,都有一个晚上,有一次报错,有一张草稿纸,有一次重新读题,有一个小孩在台灯下不肯轻易起身的背影。它们加起来,才变成那两个安静的数字。
回到家以后,他照例打开电脑。
洛谷首页还是熟悉的样子,题单、记录、练习、讨论,一切都在。屏幕的冷光照在他脸上,他伸手握住鼠标,忽然没有立刻点进下一道题,而是先去看了一眼自己的通过记录。
一串绿色静静地排列着。
他看着那一排排绿色,心里忽然很轻地动了一下。就像在冬天很深的夜里,走过很长很冷的一段路,忽然远远看见有人在窗后点了灯。灯不大,也不热烈,但你知道,自己并不是在黑里白走。
这两个月,他学会了模拟的耐心,学会了枚举的笨功夫,学会了排序的秩序感,也在递推里第一次隐约摸到“从前一步走到后一步”的意味。他仍然不是最快的孩子,甚至连“优秀得很轻松”都谈不上。可他开始真正有了一点属于竞赛生的样子——不是会几句代码,不是做出几道题,而是知道自己要怎样一寸一寸地往前拱。
窗外风吹着玻璃,发出很轻的响声。
陈紫昂吸了口气,点开下一题。
春天还没有来,可他已经在为春天做准备了。
第三章:奔向五月
济南的春天,总是来得不算利索。
三月刚冒头的时候,风里还是带着冬天没散干净的硬冷,校园里那几棵杨树光秃秃地立着,枝头泛出一点嫩意,却还远不到热闹的时候。陈紫昂每天早晨背着书包出门,楼下花坛边的泥土微微发湿,昨夜的寒气还没完全退去,空气里却已经有了一点要往上长的意思。
他知道,五月越来越近了。
山东省三年级小学生排名赛,就在五月。
这几个字一旦真正落到日历上,就和之前完全不一样了。以前它只是老师口中的一个目标,是家长群里偶尔会提到的一个比赛名,是机构里那些大一点孩子嘴里“到时候看发挥”的东西;可现在,它开始有了具体的形状,像一座放在前方的桥,桥不算很宽,却决定着很多人会往哪边走。
机构里关于比赛的气氛,也一天比一天紧起来。
小周老师上课时不再像之前那样一题一题慢慢铺开讲,而是开始更频繁地提“比赛思维”“时间分配”“怎么拿稳前四题”。白板上的板书越来越密,题目也越来越像真正的赛题:不是单纯教一个知识点,而是把几个模块揉在一起,让你自己拆。
“后面这两个月,”小周老师在一个周三晚上对全班说,“不是学会多少新东西,而是看你们能不能把已经学过的东西用出来。比赛不是课堂,没人提醒你这题该用模拟还是枚举,也不会告诉你这一步能不能优化。你得自己判断。”
他顿了顿,目光落在教室里一张张还带着稚气的脸上。
“而且三年级组,也没有你们想得那么简单。”
投影幕布上,打出了往年的题目结构。
六道题,两小时。
对应洛谷难度大致是:入门、普及-、普及-、普及/提高-、普及/提高-、普及+/提高。
考点依次是:模拟、枚举、带优化的枚举、二分加贪心、动态规划、树上问题。
教室里一下子安静下来。
连几个平时坐姿散漫的孩子都坐正了一点。
陈紫昂看着那几行字,心里也轻轻一沉。前面三题他还觉得有盼头,第四题开始就已经明显不是“多写几个循环”能解决的了,后面的动态规划和树上问题,更像是悬在那里的两扇黑门,门后有什么,他其实根本看不清。
但比赛不会因为你年纪小,就温柔一点。
接下来的日子,他还是维持着原来的节奏。
一周两次课,共五个小时;自己再练十个小时,一共十五小时。
只是到了这个阶段,那十五个小时的分量,已经和最初完全不同了。
前四个月的时候,他是在学一门陌生语言,认识语法,练基础算法,像是学会走路;而现在,他是在一条真的有很多人同时奔跑的赛道上,喘着气,低着头,想办法别被落下。
三月的第一个周六,小周老师开始讲贪心。
那天下午教室里太阳很好,光从百叶窗的缝隙里斜着照进来,一条一条落在桌面上。前排有个孩子刚削过铅笔,木屑和铅灰混在一起,散着一点很淡的味道。小周老师在白板上写下两个字:贪心。
“听起来是不是像个坏词?”他笑了一下,“其实在算法里,贪心的意思是,每一步先做当前看起来最好的选择,希望最后能得到整体最优。”
“希望”这两个字,他特地说得很清楚。
“但不是所有题都能贪心。难的是,你得看出什么时候可以,什么时候不可以。”
这句话刚说出来,陈紫昂就觉得有点难了。
因为这已经不是“老师教一个固定方法,你照着写”的层面了。模拟、枚举、排序、前缀和这些东西,至少都还有一种“看得见”的感觉,像积木一样,你能摸到边边角角;可贪心更像一种判断力。它不像公式,不像模板,更像一种很轻很薄的直觉——题目给你一团乱麻,你得看出来:先抓哪一头。
陈紫昂第一次真正碰到贪心题,是一道安排区间的基础题。
题不算复杂,老师还把思路提示得很明显。可他回家自己重做的时候,还是绕进去了。他先按开始时间排,写了一半觉得不对;又改成按结束时间排,可为什么按结束时间排就合理,他其实并没有完全想明白。草稿纸上全是他画的小线段,一条挨着一条,像堆在一起的火柴。
那天晚上书房很安静,窗外偶尔有风吹过晾衣杆,发出轻微的碰撞声。电脑屏幕映着他的脸,显得格外白。他盯着那道题,眼里慢慢浮起一点说不清的烦躁。
这和前几个月的难还不一样。
以前不会,是因为不熟;现在不会,是因为题目似乎在逼着他“想明白”。
他去问小周老师时,老师没有直接把答案塞给他,只是在纸上重新画了几条线段,把其中两条圈出来。
“你看,”老师说,“如果你先选了结束时间更晚的这一条,会不会把后面更多可能性挤掉?”
陈紫昂盯着那几条线,没说话。
“贪心不是凭感觉贪,”小周老师又说,“是你得找到一个依据,证明‘我这样选,不会吃亏’。”
不会吃亏。
这个说法很朴素,可陈紫昂一下子听懂了。
他回家以后又把那道题做了一遍。这一回,他没有急着写代码,而是先在纸上画图,把一个个选择的后果都试出来。试到后来,他忽然看见了那条线——不是代码上的,而是思路上的——从混乱里慢慢浮出来。那一瞬间,他的胸口像是被轻轻点了一下,整个人都安静下来。
他开始第一次体会到,算法题里那种“想通了”的快乐,和“做出来了”的快乐不是一回事。
后者像打中靶子,前者更像在雾里看见路。
双指针是在一周后的晚上学的。
那天外面下了一阵小雨,雨水把整座城洗得发灰发亮。陈紫昂从学校赶去机构时,裤脚被路边的水点溅湿了一点,教室里空调开得足,他一坐下,那点湿意反而更明显了。
小周老师在投影上放出一道数组题,说这道题如果暴力枚举可以做,但会慢;如果换个想法,用两个指针往前滑,很多重复计算就省掉了。
“一个左指针,一个右指针,”老师边讲边在白板上画箭头,“像两只手,一起在数组上往前摸。”
这个比喻很形象,班里几个孩子都笑了。
可陈紫昂并没有立刻放松下来。
因为他很快发现,双指针虽然写起来不复杂,难的是那个“窗口感”。什么时候右指针扩张,什么时候左指针收缩,什么时候条件满足,什么时候已经超过了,要往回挪一点——这些东西都不像排序那样规规矩矩摆在面前,而像是在水面上追一个漂来漂去的影子。
他回家练的时候,常常会把左右指针写反,或者 while 条件差一点点,结果整题就错。
有一道找区间的题,他改了整整一个晚上。
第一次,窗口没收好,答案偏大。第二次,边界条件错了,样例过不去。第三次,循环顺序一换,又把前面的正确部分弄坏了。夜一点点深下去,书房里的台灯照着桌上一片乱七八糟的草稿,像一小块孤零零发亮的岛。
陈紫昂趴在桌上,手里还捏着铅笔,鼻尖有一点酸。
他已经很久没有因为一题做不出来而这么委屈了。
三年级的孩子,委屈来得很具体。不是“我怎么这么差”,而是“我明明已经很认真了,为什么还是不行”。那种感觉像一拳打在棉花上,越使劲,越闷。
妈妈轻轻推门进来时,正好看见他盯着屏幕发呆。
“歇一会儿吧。”妈妈说。
陈紫昂没动。
“还想做。”
“那你现在是在做,还是在和题生气?”
这句话把他问住了。
他低下头,半天才小声说:“有一点生气。”
妈妈没有笑,也没有劝他算了,只是把手放到他肩上,轻轻按了按。
“那就先别和它打架。你把它当成一个小机关,慢慢找哪块没扣上。”
小机关。
陈紫昂听着这个词,又把题目打开,从头看。
果然,最后错的地方只是一个小小的 while 条件,多了个等号,整个窗口就乱了。
提交,Accepted。
绿色跳出来的时候,他先是松了一大口气,然后才在心里慢慢泛起一点迟到的高兴。那高兴不炸,也不热闹,只是一股温热,从胸口慢慢往上涌。他把身体靠回椅背,长长呼了一口气,觉得窗外那场雨像也一起落进心里,把之前的闷都冲淡了些。
前缀和则像一门看起来简单、实际很考耐心的手艺。
小周老师讲这个模块时,说得很轻描淡写:“先预处理,后面查询就快了。”
可陈紫昂真正开始做题时,才发现这几个字里面藏着多少细节。
前缀和本身并不华丽,它甚至有点朴素:你先多花一点工夫,把前面的信息都攒下来,后面就可以很快地算。这种思路有一种非常安静的聪明,不是猛冲,而是提前铺路。
陈紫昂喜欢这种感觉。
他做一维前缀和时还算顺,到了二维,就开始乱。
行和列、下标和边界、减去重叠部分再加回来,这些对一个三年级的孩子来说,已经明显不是“看两遍就会”的东西了。纸上的格子一多,他脑子就容易打结。尤其是看到一个矩形区域的时候,别的孩子好像能很自然地把它拆成几块,他却总要在纸上实实在在画出来,画成一个个小方框,才能确认自己没想错。
但他并不讨厌这种笨办法。
甚至可以说,他正是靠这种笨办法,一点一点撑着往前走。
有一天周六下午,机构留了堂小测,里面有一道前缀和题。陈紫昂做得不算快,可最后竟然是写对了。交卷以后,小周老师在他草稿纸上看了看,发现他把整个矩形区域都画出来了,还在旁边写了很多小箭头。
老师笑了一下:“你这草稿,快赶上画地图了。”
陈紫昂耳朵有点红,低头笑了笑。
“不过挺好。”小周老师说,“会画图,不丢人。很多人不是不会做,是脑子里没图,硬算,把自己算晕了。”
那句话让他心里轻轻一松。
原来自己的慢,自己的笨画法,也不完全是坏事。
二分是最后学的,也是最让他感到“这东西怎么这么像魔术”的模块。
明明答案在一整段范围里,可你并不一个一个试过去,而是每次砍掉一半。取中间,看行不行;行,就往一边缩;不行,就往另一边缩。一次,两次,三次,答案就被你一点点逼出来了。
第一次看小周老师演示的时候,陈紫昂甚至有点惊讶。
“这样也行?”
“当然行。”小周老师说,“前提是你要先看出它具有单调性。”
单调性。
又是一个不像三年级该轻易碰到的词。
陈紫昂记住了这个词,却没那么快记住它的骨头。二分最难的,根本不是 mid=(l+r)/2 这种代码,而是你要先判断:这题能不能二分?判定函数是什么?往左还是往右?边界该怎么收?
这些问题一层压一层,比起前几个月那些规规矩矩的基础算法,这里已经有了很明显的“算法思想”的味道了。
他第一次把二分和贪心放在同一题里时,整个人几乎是蒙的。
那是一道机构里模拟赛的题。题目表面上不算花哨,数据范围却暗示你不能直接枚举答案。小周老师课上讲的时候,先问大家如果直接做会不会超时,然后一点点往“二分答案”上引,再把“如何检验当前答案可不可行”落在一个贪心策略上。
那节课后半段,陈紫昂几乎一直在皱眉。
他不是完全听不懂,可就是觉得这题不像以前那样能“一把抓住”。以前的难,像是楼梯,你一级一级往上踩;这种难更像雾,老师已经站在雾对面向你招手了,你知道路在那里,可你眼下这一脚还是不太敢迈。
回家以后,他把老师的讲义摊在桌上,反反复复看了三遍。
纸页翻动的声音很轻,书房里只剩下挂钟走动的滴答声。窗外对面楼的灯一盏一盏灭下去,夜色越来越深。他拿着笔,在讲义空白处写下几个字:为什么能二分?为什么这样判定?
这两个问题,他想了很久。
最后想通的那一刻,并没有想象中那么轰轰烈烈。只是他忽然发现:原来答案越大,要求越苛刻;而检验当前答案可不可行时,那个“尽量早选”“尽量先放”的贪心操作,是在给后面留余地。
就这么一点想通,已经让他高兴了很久。
但他也第一次清楚地意识到:自己确实是刚刚起步,前面的世界还很大。像这样的题,别人也许在二年级末就已经开始碰了,而他现在才刚刚能在讲义边上写出两个“为什么”。
这个认知并不残酷,却很冷。
像春夜里推开窗,迎面吹来的那阵风。你一下子清醒了。
四月以后,复习和刷题明显进入了最后冲刺的节奏。
陈紫昂桌上的草稿纸换得越来越快,洛谷上的提交记录也越拉越长。前面学过的模拟、枚举、排序、递推,他一遍一遍回头巩固;新学的贪心、双指针、前缀和、二分,他则尽量去做那些最基础、最典型的题,把知识点先压实。
比赛越近,越能看出一个人的底子厚不厚。
有些孩子学得早,很多东西已经在手里磨得非常熟了,做题像翻自己家的抽屉;陈紫昂不一样,他很多知识点都是这半年才刚搭起来的,屋架刚有样子,砖缝里还有风。别人刷题是在提速,他刷题更像在补墙。
但他补得很认真。
周三晚课结束后,别的孩子有时会一边收拾书包一边讨论哪题更优雅,哪题是不是还能再用更快的做法;陈紫昂常常还会坐在位置上,盯着自己那道没完全懂透的题,看一遍题解,再自己关掉题解重写一遍。
那种认真有点笨,却有一种很结实的力量。
四月中旬,机构组织了一次接近正式比赛规格的模拟测。
两小时,六道题。
教室里空调开得有点低,纸卷发下来时发出齐刷刷的声响。陈紫昂捏着笔,先从头到尾扫题。第一题模拟,第二题枚举,第三题带优化的枚举,他心里都还算有底。第四题一看就带着二分答案的味道,后面动态规划和树上问题则明显阴下来,像两块压在纸面上的乌云。
铃声一响,教室里只剩下敲键盘和翻纸的声音。
陈紫昂先做前三题,做得不算特别快,却比较稳。写到第四题时,他明显卡了一下。题目里的“可行性”需要自己去想,他在草稿纸上列了好几组样例,心里那条线总差一点才抓住。时间一分一分过去,教室里有孩子已经开始看第五题了,他却还在第四题前面挣扎。
那一瞬间,他的手心全是汗。
他忽然很害怕。
害怕不是因为题难,而是因为他知道:这就是比赛时最真实的样子。前面的人不会停下来等你想明白。你卡住的这十分钟、二十分钟,可能已经足够把别人和你之间再拉开一大截。
他咬了咬牙,硬是把第四题一点一点啃下来。
最后成绩出来,四题多一点,排在班里中上。
不难看,但也绝不惊艳。
小周老师把卷子发到他手上,说:“前四题思路基本没问题,说明基础在那儿。后面还是要练速度,尤其是第四题这种有一点算法思想的,不能一紧张就乱。”
陈紫昂点点头,手里捏着那张卷子,心情有些复杂。
他既为自己能稳住前四题而高兴,又清楚地知道:在真正的赛场上,像他这样的孩子其实很多。你不是不会,只是还不够快,不够熟,不够早。
而在这个世界里,不够早,有时本身就是一种落后。
五月终于到了。
比赛前一周,济南的树已经全绿了。校园操场边的月季开了一圈,风吹过来,带着一种暖洋洋的草木气。可陈紫昂却没有多少心思看这些。他的状态变得异常安静,像一根已经绷紧了的线。
最后几天,小周老师不再给他压太多新题,只让他回看错题、练手感、模拟时间分配。
“第一题、第二题、第三题尽量快而稳,”老师说,“第四题要敢想,但别死耗。后面两题,你能看出一点门道最好,看不出来也别把前面的分丢了。”
“别把前面的分丢了。”
这话简单得近乎残酷。
因为它其实已经默认了:有些门槛,对现在的你来说,确实还够不到。
比赛前一晚,陈紫昂早早洗漱完,躺在床上,却迟迟睡不着。窗帘缝里透进来一点城市夜里的微光,房间里很安静,他能听见自己轻轻的呼吸声。
他在脑子里一遍一遍过题型。
模拟怎么审题,枚举怎么控制边界,带优化的枚举常见有哪些思路,二分答案时要先看什么,贪心判定要不要画图……那些知识点像许多小小的碎片,在他脑子里来回漂着,拼不成一张完整的图,却也没有散。
妈妈进来替他掖被子的时候,看见他眼睛还睁着。
“紧张?”妈妈轻声问。
陈紫昂点了点头。
妈妈坐在床边,摸了摸他的额头。“紧张正常。你就当去把自己会的东西写出来。写到哪一步,就算哪一步。”
“如果没考好呢?”他忽然问。
这个问题问得很轻,可房间里一下子安静了。
妈妈沉默了几秒,才说:“没考好,也不是天塌了。你才三年级。”
这话当然没错。
可陈紫昂不知道为什么,听了之后心里反而更酸了一点。因为他隐约知道,在这个世界里,“你才三年级”并不是天然的护身符。太多三年级的孩子,已经早早冲到了更前面。你年纪小,不代表赛道会等你。
但他最后还是慢慢闭上了眼睛。
第二天总会来的。
比赛当天,天很亮。
考场设在一所学校的信息机房里。走廊很长,地砖擦得发亮,窗外树影被太阳照得发白。家长们站在警戒线外,低声说话,语气都压着,像生怕惊动什么。很多孩子比陈紫昂高,也比他更老练,背着书包走进考场时神情很平,像已经很熟悉这一切。
陈紫昂走在里面,手心有些潮。
机房里一排排电脑整齐摆着,显示器冷白色的光照着每张小脸。空调开得有点足,椅子推拉时发出轻轻的摩擦声。监考老师宣读规则的时候,他听得很认真,可耳朵里又像隔着一层水,只能听见最关键的几个词:两小时,六道题,独立作答,时间到自动结束。
屏幕亮起,题目发下来的那一刻,他的心猛地跳了一下。
第一题果然是模拟。
他深吸一口气,告诉自己:慢一点,看清楚。
题目不算难,他按部就班地写,调样例,提交。Accepted。
第二题是枚举。
他速度快了一些,思路也顺,几层循环一摆,样例过,交上去,还是绿。
第三题是带优化的枚举。
这是他这段时间练得最多的一类。不是纯粹暴力,而是得在暴力外面包一层小心思,让原本太慢的做法降下来一点。他看题时心里先是发紧,可读到中间,忽然觉得某个地方很熟——像之前做过的一道洛谷题的变形。他赶紧在草稿纸上列条件,试范围,最后真的把做法拧出来了。
第三题也过了。
到这里时,他心里的紧张已经稍微松了一点,可时间也过去不少了。
第四题一翻开,果然是二分加贪心。
题目比机构模拟测的还要绕一点,数据范围很大,光看就知道不能老老实实试。陈紫昂盯着题面,后背一点点绷紧。他先写了几个样例,找单调性,再想判定函数。中途有一度,他几乎觉得自己又要像模拟测那次一样卡住了。
可这一次,他没有慌得那么厉害。
也许是前面三题的顺利给了他一点底气,也许是这两个月那些反复重做、重画图、重想“为什么”的夜晚,确实在脑子里留下了点什么。他一边咬着嘴唇,一边在纸上画条件,把“当前答案可行”一点点翻译成贪心操作。
时间一分一秒地过去。
终于,在还剩下不多的时间里,他把第四题也交上去了。
评测转圈的时候,他几乎能听见自己的心跳。
几秒后,绿色跳出来。
那一瞬间,他差点想直接往椅背上一靠,长长地出一口气。可他忍住了,马上去看第五题。
第五题是动态规划。
题目不算花哨,却带着一种很冷的难。状态怎么设,转移怎么做,一眼看过去根本没有入口。陈紫昂盯着屏幕,脑子里的思路像一堆细线,东一根西一根,怎么也拢不到一起。他列了几个最小情况,试图从中找规律,可找到一半,时间又逼着他往前走。
第六题更让人发怵。
树上问题。
光这四个字,就足以让很多三年级孩子在心里先退一步。陈紫昂也不例外。他其实学过一点最基础的树结构,知道父子节点,知道遍历,可真正拿它做题,又是另一个世界。题目像一片枝杈繁密的树林,他站在入口处,远远看见里面有路,却怎么都找不到该先迈哪只脚。
他尝试写了点部分分,想拿一点是一点。
可时间终究不够了。
结束铃声响起的时候,整个机房像被人同时按了暂停键。有人一下子瘫回椅子里,有人还盯着屏幕发愣,有人咬着嘴唇,显然在懊恼最后几分钟没能再推进一点。
陈紫昂坐在那里,手还搭在键盘上,指尖发凉。
他做出了四题。
准确地说,是稳稳做出了四题,后面两题只摸到了一点边。
走出考场的时候,走廊里的光亮得有些晃眼。妈妈一眼就看见了他,立刻迎上来:“怎么样?”
陈紫昂先抿了抿嘴,像是在确认自己该怎么形容这场考试,最后才说:“四题。”
妈妈一下子松了口气:“那不错啊。”
他点点头,却没有想象中那么兴奋。
因为他自己知道,这个“四题”是个什么位置。它意味着基础分拿住了,说明这半年没有白学;可也意味着,他和那些真正跑在前面的孩子之间,还隔着后面那两道题,隔着更早的起步,更厚的底子,更熟的手感。
太阳很暖,照在校园的水泥地上,反出一点白光。周围有家长已经在低声讨论谁家孩子说第五题像某道经典 DP,谁家孩子第六题树上 DFS 写了一半。那些词一阵阵飘过来,像风吹过耳边。
陈紫昂安静地听着,没说话。
他忽然觉得,自己已经很努力了,可“很努力”这件事,在这样的赛场上,很多时候只够换来一句“还不错”。
成绩出来,是几天后的一个傍晚。
那天晚饭刚端上桌,妈妈手机震了一下。家长群、机构群、朋友转来的消息,几乎同时弹出来。山东省三年级组排名赛成绩开放查询了。
书房里的灯一下子亮了。
妈妈坐在电脑前,手指点开页面时都有点紧。陈紫昂站在旁边,双手背在身后,嘴唇抿得很紧。房间里很安静,只有鼠标点击的声音,还有网页加载时短暂的空白。
成绩跳出来的时候,三个人都先愣了一下。
前 30%。
二等奖。
不是特别高,也绝不差。
对于一个三年级、零基础起步、只学了半年的孩子来说,这已经是一个完全能说明问题的成绩了。它证明他有一定天赋,基础也扎实,这条路完全可以继续往下走。
妈妈第一反应是真心高兴的。
“很好啊。”她回头看他,眼睛都亮了一点,“前 30% 已经很好了。”
陈紫昂也笑了。
那笑不是很大,却很真。他当然知道自己这半年都做了什么,知道那四题是怎么一点点啃下来的,也知道二等奖不是从天上掉下来的。那一瞬间,他心里有一种很踏实的满足感,像自己背着小小的书包走了很长一段路,终于走到一个能站住脚的地方。
可这种高兴并没有持续太久。
因为再往下看,他也看见了一等奖的线,看见更靠前的名次,看见那些真正能进入当地顶级初中视线的孩子大概都站在哪个位置。
他差了一截。
不是差得遥不可及,却也是明明白白的一截。
那天晚上,家里气氛有些微妙。
饭桌上并没有谁说重话,爸爸妈妈也都在肯定这个成绩,夸他能拿二等奖已经很不错,夸他半年学到这样很不容易。可成年人心里的算盘,往往不是靠说出口才存在的。陈紫昂虽然小,却已经能感觉到:这个成绩值得继续,但还不够惊艳,不够让人眼前一亮,不够让那些最顶级的学校立刻在名单上圈出他的名字。
饭后,爸爸和妈妈在客厅里轻声商量,声音压得很低。
“继续学肯定能继续学。”妈妈说。
“嗯,底子还行。”爸爸回答,“但现在这个成绩,还进不了最顶尖那几所的眼。”
“看四年级吧。”
“对。四年级要是能冲到一等奖,就继续往下投。要是还是上不去……”
后半句没有说完。
可房门没有关严,陈紫昂还是听见了。
他坐在书房里,面前摊着一本没翻开的题解,屏幕已经黑了。台灯的光落在书页上,很白,也很静。他其实并没有全听懂大人的规划里那些更深的含义,可有一句他听懂了——
四年级如果学得不错,能拿一等奖,就继续。
否则,只能放弃。
“放弃”这个词,像一颗很小但很硬的石子,轻轻落进了他心里。
他没有哭,也没有立刻难过得受不了。只是突然觉得,自己这半年走过的路,好像一下子被放在了一杆秤上。秤的一边是那些数不清的夜晚、草稿纸、提交记录、Accepted;另一边,则只是冷静得近乎简单的一句话:够不够继续。
他坐在那里,安静了很久。
窗外已经全黑了。对面楼的灯亮着,偶尔有谁家电视屏幕闪一下蓝光。楼下不知道是谁家孩子还在跑,笑声隐隐约约传上来,又很快散掉。书房里只有他一个人,静得连翻一页纸的声音都会显得很大。
他忽然想起自己第一次注册洛谷时,看到那一整片题库,觉得像站在一座巨大城市门口。那时候他只是觉得新鲜,觉得远。可现在,他已经往里走了半年,才终于慢慢明白:这座城里不是所有门都会一直开着。
有些门,会看你走得够不够快。
周三去上课的时候,小周老师也知道了成绩。
机构里有人比陈紫昂考得更高,有人冲到了一等奖,也有人没发挥好,脸色一直闷闷的。成绩像春天里的一场雨,落到每个孩子身上,发出的声音都不一样。
小周老师把陈紫昂叫到一边,问他:“自己怎么看这次成绩?”
陈紫昂低头想了一会儿,说:“还可以……但是不够好。”
小周老师看了他两秒,笑了笑。
“知道不够好,是好事。”他说,“但别把‘不够好’理解成‘不行’。你学了半年,能做到这个位置,已经说明很多问题了。只是你前面确实有人起步更早,基础更厚,这个没法骗自己。”
陈紫昂点点头。
他没有被这句话刺痛,反而觉得一种奇异的安定。因为小周老师没有空洞地夸他“你已经特别棒了”,也没有用那种一听就站不住的话去哄他。他只是把事实摆出来:你可以,但你还差一段。差一段,不等于没希望;可要追,就得继续熬。
下课的时候,窗外晚霞很浅,像一层快化掉的糖色。陈紫昂背着书包,走到楼下,风吹过来,已经有了点初夏的暖意。妈妈照例来接他,问他老师怎么说。
“老师说,可以继续学。”他答。
妈妈笑了一下:“那就继续。”
陈紫昂“嗯”了一声。
车子启动的时候,他回头看了一眼写字楼七层那扇亮着灯的窗。玻璃后面,别的孩子还坐在那里,有人在改题,有人在听老师讲新东西,有人的脸上已经有一点明显的得意,也有人还带着没考好的失落。
这个世界就是这样。
有人很早就冲在前面,有人还在追;有人考完一场比赛,换来更多资源和更亮的目光;有人考完以后,只得到一个“可以继续试试看”的资格。可即便如此,那条路也还是摆在那里,长长地往前延伸。
陈紫昂坐在妈妈车后座上,晚风吹着他的额发,天边最后一点霞光慢慢暗下去。他忽然没有那么难过了。
因为他想起考场里那四道被自己稳稳做出来的题,想起那些深夜里一点一点想明白的“为什么”,想起洛谷主页上一排排绿色,想起这半年里那个总坐在台灯下、不太肯轻易认输的自己。
他不是最快的孩子,也还远没到能让顶级初中一眼看中的地步。
可他已经站上这条路了。
而且,他还想再往前走一段。
哪怕只是一段。
哪怕前面的人影,依旧那么多。
第四章:那个很长的夏天
七月一到,济南就像忽然被谁把火打开了。
太阳一早就挂得很高,白得刺眼,光从窗帘缝里直直扎进屋里,把书桌边缘照出一条发烫的亮线。小区里蝉鸣一阵一阵地炸开,树叶被晒得发亮,连空气都像在微微晃。别人家的孩子开始商量暑假去哪里玩,去海边,去姥姥家,去上游泳班;陈紫昂的暑假却从第一周开始,就有了另一种更具体、更沉甸甸的样子。
课程表被重新排过了。
从原来的一周两次课,变成一周四次;总课时从五小时变成十小时。除此之外,他每天还要自己练两小时。算下来,一周接近三十小时。
这个时间,在山东三升四的学生里,已经算得上是认真往上冲的一档了。虽然还不是最狠的那种——家长圈里总有人提起某个孩子一周学四十小时,上午刷题,下午听课,晚上复盘,像一台拧到极限的小机器——但对陈紫昂来说,这个暑假也已经很长、很满、很不一样了。
妈妈给他定计划的时候,其实有过犹豫。
“要不要再多加一点?”有一次她和爸爸在客厅里低声商量,“现在暑假,确实是追进度的最好时候。”
“再多就太紧了。”爸爸摇头,“咱们也不是非得把孩子压到一点气都没有。脑子灵,能学进去,比单纯堆时间更重要。”
“我也觉得别学那么卷。”妈妈说。
最后定下来的,就是这个一周近三十小时的强度。
算不上疯狂,却也绝不轻松。
陈紫昂一开始并没有完全意识到,这三十小时意味着什么。他只是觉得,暑假一开始,日子忽然变得很整齐:起床,吃饭,上课,练题,吃饭,再练题。一天被切成一小块一小块,每一块上面都写着“算法”或者“题目”。刚开始的那几天,他甚至还有一点隐隐的兴奋,觉得自己像是正式进入了某种更厉害的训练状态。
直到第三周,他才真正感到累。
那种累不是一下子扑过来的,而是慢慢爬上身的。早上起床时,眼睛还有点发涩;下午做题做到一半,会忽然发一会儿呆;晚上洗澡的时候,脑子里还在飘数组下标、循环条件、状态转移。蝉鸣和风扇声混在一起,整座城市都热得发白,他坐在书桌前,手臂贴着桌面,能感觉到自己皮肤上的一点汗意。
可也正是在这样漫长、发热、几乎没有什么空隙的夏天里,他第一次明显地长快了。
不是个子,而是算法里的那部分自己。
暑假的第一阶段,小周老师先带他学的是线性表。
栈、队列、链表。
这些名字听起来不算花哨,甚至还有点古怪。可小周老师一讲起来,教室里很快就有了画面。
“栈像什么?”老师拿着笔,在白板上画出一摞盘子,“像食堂里叠起来的盘子,后放上去的,先拿走。后进先出。”
“队列呢?”他又在旁边画了几个人,“像排队打饭,谁先来谁先走。先进先出。”
说到链表的时候,他在白板上画了一个个小方框,用箭头连起来,像一串手拉手往前走的小人。陈紫昂盯着那一串箭头,觉得很新鲜。数组像一整排固定的小格子,整整齐齐地排在那里;链表却更像是活的,一个节点拉着下一个节点,指到哪里,哪里就是路。
这些东西比起他前面学过的模拟、枚举,多了一点“结构”的味道。
不是只告诉你怎么做一题,而是在告诉你:数据本身也可以有不同的组织方式。不同的组织方式,会带来不同的处理办法。
陈紫昂最先喜欢上的是栈。
也许因为它太容易想象了。括号匹配、表达式处理、回退操作……很多看起来有点绕的问题,一旦放到“后进先出”的盒子里,就突然变得顺了。第一次用栈做括号匹配题时,他看着字符一个一个压进去,再在合适的时候弹出来,心里生出一种很奇妙的满足感,像终于给一团乱线找到了那个能把它顺开的线轴。
但链表对他来说就难多了。
数组的下标是看得见摸得着的,a[1]、a[2]、a[3],一步一步都站在地上;链表却总像飘着。特别是“下一个是谁”“删掉一个节点以后要怎么接回去”这些地方,一不小心就会乱。小周老师讲的时候,他勉强还能跟上;回家自己做题,一到指针和连接关系的地方,他就容易发懵。
有一天晚上,他在书桌前写一题简单链表模拟,写到一半突然发现整条链断了。
不是现实里的链,而是代码里的那条。原本应该一环扣一环的节点,在某一步操作之后,后面全找不到了。屏幕上的程序运行完,输出空空的,像一扇明明应该打开却怎么也推不开的门。
陈紫昂盯着那份代码,脑子里一片乱。
他试着重画图。一个框,一个箭头,再一个框,再一个箭头。可画着画着,纸上的箭头越缠越多,他自己都快看不懂了。窗外热风吹得纱窗轻轻响,书房里风扇呼呼地转,吹不散他额头上慢慢冒出来的一层细汗。
他有点急了。
偏偏链表这种东西,一急最容易全盘乱套。
最后还是小周老师在第二天课上帮他把那条“断链”重新接了起来。老师没批评他,只是拿红笔在讲义边上写了几个字:先画图,再改指向。
“链表不能光靠脑补。”老师说,“先看清楚谁连着谁,再动手。不然你自己把自己绕死。”
陈紫昂点点头。
回家以后,他把这几个字誊到了自己暑假的笔记本扉页上。那本子很普通,封皮是深蓝色的,边角很快就磨白了。可里面一页一页,记的都是他这个夏天一点一点掰开的东西。
七月中旬开始,课程往初等数论那边挪。
因数、倍数、质数、质数筛。
这些词听起来不像编程,倒更像数学课上会出现的东西。但放进算法里以后,它们忽然有了新的锋利感。一个数能被谁整除、哪些数是质数、如何快速筛掉一大片合数,这些原本零零散散的概念,一旦被程序接住,就变成了很实在的工具。
陈紫昂起初很喜欢“质数”这个概念。
他觉得质数有一种奇怪的孤独感。只能被 1 和自己整除,像一群有点倔、又有点干净的数字。小周老师在讲台上写下 2、3、5、7、11、13 时,他甚至觉得这些数字和别的数字站在一起时,气质都不一样。
可真正做题时,浪漫很快就退掉了,剩下的全是边界和细节。
尤其是质数筛。
最开始,小周老师先教最朴素的筛法。一个一个数过去,把倍数划掉。投影幕布上那些数字被一层层划掉的时候,陈紫昂觉得挺爽,像在一张大网里把不该留下的鱼都捞出去,只把最干净的留下。可当题目数据一大,或者和别的知识点揉在一起,他就很容易乱。
有一道题要统计区间内质数个数,再配上一点前缀和的味道。题目不算夸张,但对暑假里还在搭底子的他来说,已经足够让人发紧了。
那晚他在书桌前做了快一个小时。
先筛,再统计,再回答查询。思路明明都见过,可真落在代码里,变量一多,数组一多,他就会忘了自己当前到底在处理哪一步。最烦的是,样例又过了,提交上去却还是 WA,像系统故意不肯让他轻松。
他盯着那一抹红色,心里忽然冒出一种很幼稚、很直接的念头:为什么一个题非要这么多东西一起出现?
但题目才不管你是不是三年级。
它们只是安静地摆在那里,要求你看清,想通,再写对。
陈紫昂重新把草稿纸铺平,先把筛法流程画了一遍,又把区间统计的方法单独列出来。天已经很晚了,楼下小区的路灯把树影投到窗帘上,一晃一晃的。房间里只有键盘敲击声和偶尔翻纸的声音。他在那样的安静里一点一点地重搭思路,像在黑暗里重新拼一台拆散了的小机器。
最后过掉的时候,他没有立刻喊妈妈,也没有特别激动。
他只是低头看着屏幕上那一块绿色,轻轻抿了一下嘴,心里却很踏实。
这种踏实,和前几个月比起来已经不一样了。以前过一道题,更像“我终于做出来了”;现在则更像“我把这些分散的东西真的接起来了”。
这是成长最慢,也最实在的部分。
真正让这个暑假显出分量的,是动态规划。
或者说,是简单动态规划。
一维线性 DP,基础的背包 DP。
小周老师第一次在白板上写下 f[i] 的时候,教室里其实没什么特别反应。毕竟这个符号大家并不陌生,以前递推时也见过。可老师接着说:“动态规划和递推很像,但它不只是往下推几步,它是在一堆可能的状态里,找最优的那个。”
最优。
状态。
这两个词一摆出来,教室里的空气好像都微微紧了一下。
因为大家都知道,这已经开始往更里面走了。
陈紫昂学一维 DP 的第一天,整个人都很安静。
他听老师讲“走楼梯”“最大和”“最少代价”这些典型题,边听边在本子上写 f[1]、f[2]、f[3]。最开始,他还能把它理解成一种稍微复杂一点的递推:前面的值推后面的值,条件稍微多一点,答案要挑最好的。
可一到背包,他就明显吃力了。
“每个物品选还是不选”“容量从前往后还是从后往前”“二维状态怎么压成一维”——这些东西对一个刚升四年级的孩子来说,确实已经不是自然会的层次了。尤其是“为什么 01 背包要倒着循环”,这一点像根细刺,卡在他脑子里,怎么都不顺。
小周老师给了个比喻,说背包像在一排柜子里放东西,倒着放,才不会让同一个东西在这一轮里被重复拿到。陈紫昂听完,似懂非懂。回家以后,他自己又拿本子画柜子,画物品,画容量。
那几天他的草稿纸格外多。
不是因为题量特别大,而是因为每一道 DP 题,他都要在纸上把状态一个格子一个格子地写出来。别的孩子可能看几眼就能在脑子里滚过去,他不行。他得写,得画,得一格格看着这些数字从空白里长出来,才能确认自己真的抓住了那条线。
暑假的午后总是很长。
窗外热得发白,空调开着,房间里安静得只剩下机器轻轻的嗡鸣。陈紫昂坐在书桌前,手边一杯温水,面前摊着讲义和草稿纸。他一会儿皱着眉头写状态,一会儿停下来发呆,再一会儿重新低头,把刚才那一步又写一遍。
有一次妈妈端水果进来,看见他面前那张写满 f[i] 的纸,忍不住笑了:“这像密码一样。”
陈紫昂抬头,认真地说:“老师说这个以后很重要。”
妈妈点点头,把水果放下,没再说话。
她其实也看不懂这些状态转移式子。但她看得出来,儿子在用一种很慢、很笨、却很有力气的方式,把这些东西一点一点往自己脑子里搬。
而这比“懂不懂”本身,更让人心里发热。
八月上旬,暑假已经走过大半。
院子里的树叶长得正盛,白天的热浪还是一阵一阵往上冒,但夜里已经偶尔能吹来一点不那么烫的风。陈紫昂的状态也和七月刚开始时不一样了。
他还是会累,还是会卡题,还是会看见某些题面时下意识心里一紧。可那种“新学东西总要先怕一怕”的感觉,已经没那么重了。他开始真正适应这种高强度的节奏,也开始在连续刷题里体会到一种以前没有的顺手感。
更重要的是,他的题单开始往上拱了。
入门越来越稳,普及- 越来越厚,普及/提高- 也开始一题一题地接触。虽然很多普及/提高- 的题他还是要磨很久,甚至有时只能做出一半或者看题解后再补,但他至少已经能站到那个门口了。
这是很了不起的一步。
如果把半年前那个还在为分号和数组下标发愁的小孩拉来看看现在的题单,连他自己都会觉得有点不可思议。
暑假后半段,小周老师开始进行综合训练。
这也是整个假期最像“真正打仗”的阶段。
每次课,老师都从洛谷上挑一套入门赛下来,八道题,从入门到普及/提高-,难度一层一层往上抬。不是单点教学,而是让陈紫昂一道一道做,从最基础的枚举模拟,到二分套路题,再到线性动态规划套路题,让他在有限时间里自己判断、自己分配、自己决定哪里该快,哪里该停。
“这段时间别太把自己当成在‘学知识点’。”小周老师说,“更要把自己当成在‘做一套题’。比赛不会把知识点拆。前两道门很亮,看得见里面;第三、第四道开始复杂;再往后,那种“门后有东西,但你暂时够不太着”的感觉就越来越明显了。
他做第一套时,前面几题还行,到了中段就明显乱了节奏。有一道二分套路题,他明明看出了大概方向,却因为判定函数没想清楚,来回磨了太久,后面两道 DP 连充分思考的时间都没剩下。下课时,窗外天还亮着,他坐在电脑前,心情有点闷。
小周老师把他的草稿纸拿起来翻了翻,说:“你不是不会,是容易在卡住的时候舍不得走。”
“可是感觉再想一下就能出来。”陈紫昂小声说。
“比赛里很多题都这样。”老师说,“差一点和真的出来,是两回事。你得学会认什么时候该先拿后面的分。”
这句话很现实,也很难听。
但陈紫昂知道,老师说得对。
接下来的训练里,他开始逼自己改这个毛病。前面能拿的分尽量快拿,中间卡住先做标记,往后看,再回来。这样的切换对一个三年级刚升四年级的孩子并不容易,他常常刚从一题里拔出来,脑子还停在那个题的思路里,另一题又撞上来,整个人会有一瞬间的空白。
可练多了,他真的慢慢会了。
会在题目里闻出一点熟悉的味道:这是前缀和的壳,还是双指针的影子?这是线性 DP 套路,还是二分答案的模板变形?他当然还远没到“看一眼就知道”的程度,但至少,过去那种面对整套题时的茫然感,已经开始退了。
有一天下午,他在综合训练里做到一道线性 DP 题。
题面不算长,状态也不花,但如果没有接触过相近套路,很难一眼抓住。陈紫昂先是卡了几分钟,后来忽然想起老师前些天讲的一道题,状态转移几乎是同一路数。他赶紧在纸上写出 f[i] 的含义,一步一步列,越列越顺,最后居然真的独立写出来了。
提交,Accepted。
那一瞬间,他坐在椅子上,心里忽然涌起一种很强烈的感觉——
自己真的在长。
不是“学得很多”的那种长,而是某些原本完全够不着的东西,现在已经能摸到了。哪怕只是指尖轻轻碰一下,也已经和以前不一样了。
整个八月,济南的天都很高。
有时候下午下过一阵暴雨,雨点砸在窗户上,天地像一下子灰了;可雨停以后,太阳很快又出来,地面蒸起白白的热气,树叶被洗得格外亮。陈紫昂就在这样的晴晴雨雨里,一点一点往前跑。
有几次,他也确实会烦。
尤其是连续两三天状态不好,题做得慢,综合训练又被中段的题卡住的时候,那种烦会很真实地冒出来。不是想放弃,而是会忽然觉得,这个暑假怎么这么长,这些题怎么做也做不完,自己怎么总还差一点。
有一天晚上,他练完题从书房出来,靠在客厅门边,忽然问妈妈:“为什么别人可以学四十小时啊?”
妈妈正在切西瓜,听见这话停了一下。
“怎么突然问这个?”
“老师说有的人暑假一周学四十小时。”他低头看着地板,“他们会不会比我快很多?”
妈妈没有立刻回答。
厨房的灯光落下来,照得西瓜瓤红得发亮。窗外蝉声还是密,整座城市像被夏天包得很紧。过了一会儿,妈妈才说:“会快一些吧。可咱们家没想让你过成那个样子。”
“为什么?”
“因为学得好,不只是靠时间。”妈妈把切好的西瓜递给他,“你脑子灵,肯动脑,也肯熬。这已经很好了。再说了,真把自己压太狠,心里没劲了,走不远。”
陈紫昂接过西瓜,没说话。
他其实未必完全明白“走不远”是什么意思。可那天晚上,他心里那股发紧的劲,还是被慢慢按下去了一点。
他忽然意识到,自己的这个暑假,也许已经够长、够热、够累了。
而且,他确实在往前走。
八月底,暑假快结束的时候,小周老师给他做了一次总的盘点。
教室里空调吹着,窗外天色已经有了一点夏末的松动。老师把他这几个月的记录翻出来,题数、模拟情况、综合训练表现,一项一项看过去。
最后停在平台数据上:
入门 250 题。
普及- 300 题。
普及/提高- 100 题。
这三个数字静静地摆在那里,没有夸张的气势,却有一种非常扎实的重量。
小周老师看着他,点了点头:“这个暑假练得不错。”
陈紫昂坐在椅子上,背挺得很直。
“入门和普及- 基本已经压得比较实了,普及/提高- 也碰了不少。”小周老师顿了顿,“更重要的是,你不是只会前面那几种基础题了。现在碰到一些带套路的二分,一些基础线性 DP,你已经有感觉了。”
“有感觉”这三个字,比“会做”还让陈紫昂高兴。
因为他知道,真正的成长常常就是先从“有感觉”开始。你还不能保证每题都做对,但你已经能闻到那道题属于哪一类,已经知道从哪边下手不算太离谱。这种感觉,是一张题单、一张题单磨出来的。
小周老师又说:“如果你现在回到五月那场三年级排名赛,第五题那个普及/提高- 的动态规划,应该是有机会做出来的。”
这句话一说完,陈紫昂的心口像被谁轻轻撞了一下。
他愣了愣,抬起头:“真的?”
“真的。”老师笑了笑,“第六题树上问题还是难,但第五题,按你现在的水平,已经不是完全没门了。”
那一瞬间,整个暑假的热、累、烦、闷,好像都忽然有了一个很具体的落点。
原来自己不是在原地打转。
原来那些中午顶着蝉鸣写下的状态转移,那些晚上在风扇底下反复重做的二分套路题,那些链表断了又接、背包转移想了又想的时刻,真的都在把他一点一点往前推。
不是特别戏剧化,不是一下子飞起来。
但确实在往前。
回家的路上,天已经有了一点秋天前夕的清亮。
虽然还热,可风吹过来的时候,已经不再是七月那种闷得人喘不过气的热浪了。晚霞在楼群之间铺开,天边是很浅很浅的橘红色,像被太阳烤软了一层。
妈妈骑车,陈紫昂坐在后座,双手抓着衣角,脑子里却还在想那句话:
如果回到五月,第五题我应该能做出来。
这句话并不能改变已经过去的那场比赛,也不能让他一下子就冲进当地顶级初中的视线里。可它像一枚很小的勋章,安安静静地别在这个夏天的末尾,证明他不是白过的。
他忽然想起五月考场里,自己盯着第五题那片冷冷的题面,怎么想都摸不出入口;又想起现在,自己已经能在综合训练里独立写出基础线性 DP 的套路题。两种自己叠在一起,差的其实只是一个暑假。
一个很长、很热、很累,却也很值的暑假。
回到家以后,他没有立刻去玩,也没有像小时候那样一进门先找零食。他先把书包放下,然后下意识地走进书房,开电脑,登洛谷。
页面亮起来的时候,他看见自己的通过记录又往后延了一截。
绿色一排一排,很安静。
他盯着那些记录看了好一会儿,忽然觉得,自己这个夏天像是在一条看不见的坡道上慢慢往上爬。坡不算陡到绝望,可也绝不轻松。你爬的时候总嫌热,总觉得累,总觉得前面还有很长;可等真的停下来回头看,才会发现脚下已经远了不少。
外面有人在楼下喊回家吃饭,声音被晚风送上来,带着很日常的热闹。书房里却还是那种熟悉的安静,只有电脑风扇轻轻转着。
陈紫昂坐在椅子上,手放在鼠标上,忽然有一种很安稳的满足感。
这个暑假,他学了栈和队列,学了链表,学了因数、倍数、质数和筛法,学了简单动态规划,学了怎么在一整套题里判断、取舍、往前冲。他没有被压到最狠,也没有真的变成那种一周四十小时的孩子;可他已经把自己往前拱出了一大截。
而且这一大截,不只是题数上的,不只是平台记录上的。
还是他心里那种“我好像真的可以继续往下学”的感觉,第一次变得这么清楚。
窗外的天一点点暗了,晚霞褪成了温柔的灰蓝。暑假的尾巴正轻轻收拢,新的学期很快又会开始。更高一级的题、更难一点的比赛、更早起步的对手,都还在前面等着他。
可至少在这个夏天结束的时候,他已经不再只是那个站在门口的小孩了。
他还没走很远。
但他已经真正走进来了。
第五章:秋令营见真章
九月一到,济南的热气一下子收了不少。
暑假的蝉声还没完全退干净,可清晨的风已经不再黏人了。学校门口的梧桐叶开始有一点发暗,太阳照下来,也不像七八月那样白得发烫。陈紫昂背着书包走进新学期,个子似乎比六月高了一点,书包也比以前沉了一些——里面除了语文数学英语的课本,还塞着一本深蓝色的算法笔记,一个装满草稿纸的透明文件袋,和一支被他捏得有点发亮的自动铅笔。
这个暑假,他练了很多题。
多到有时候晚上躺下去,脑子里还在飘状态转移和二分边界;多到再回看五月那场三年级排名赛时,他甚至能隐约感觉到:如果把现在的自己放回那天考场,第五题那道动态规划,他大概是有机会拿下来的。
可“感觉”终究只是感觉。
没有真正站到一群四年级学生中间,没有真的在陌生考场里比过,没有跟那些从更早时候就开始练的人同场跑过,他其实并不知道,自己现在大概处在一个什么位置。
这是九月初的陈紫昂最在意、也最说不清的一件事。
他像是刚刚练了很久的拳,终于要第一次上台试打。你知道自己不是全无准备,知道自己比半年前强了很多,可到底能站住几回合,能不能不被人第一下就推下去,心里还是没有底。
也正是在这种没底的时候,一封邀请来了。
那天傍晚,妈妈刚把菜端上桌,手机忽然震了一下。她低头看了一眼,先是愣住,紧接着神情就变了,连说话声音都微微提起来:“紫昂,来一下。”
陈紫昂从书房里探出头,手里还捏着笔。
“怎么了?”
“之前投的秋令营简历,有学校回复了。”
他说不上来心里那一下是什么感觉,像有只小锤子在胸口轻轻敲了一下。妈妈把手机递给爸爸,两个人靠在一起看。屏幕上那行字不长,内容却很清楚:邀请陈紫昂参加本月底前的一次秋令营测试,地点在当地一所二线水平的初中。
这所学校本身不算最顶尖,可圈子里都知道,它一直被当地一所头部初中带着,很多课程、很多活动,都和那边有些联系。换句话说,如果孩子在秋令营里表现犹豫。
妈妈也点头:“当然去。先别想那么远,先去看一看。”
陈紫昂站在餐桌边,听着“大概有机会”“如果特别出色”这些词,心里却没有立刻涌出多大的兴奋,反而先浮起一点小小的紧张。
因为他听得懂那句话的另一半。
前提是表现得特别出色。
而他很清楚,自己这一路走到现在,虽然绝不算差,可离“特别出色”这四个字,似乎总还是差一点什么。
那天晚上,他回到书房,打开洛谷,却没像平时那样立刻点进题单。他先看了一会儿自己的提交记录:入门、普及-、普及/提高-,绿色一排一排,安静地铺开。那些记录明明是他自己一题一题敲出来的,可在“秋令营”三个字面前,他却忽然觉得它们有点像练兵场上的成绩单——是基础,是底气,但还不是真刀真枪。
他盯着屏幕看了很久,最后只是在本子上写下了几个字:
第一次实战。
秋令营在一个周六。
那天早晨,天很高,风也清。妈妈特意让他穿了一件干净的白色短袖,外面套浅蓝色外套,书包里装了准考证、水杯、几支备用笔和一摞草稿纸。去学校的路上,街边的早餐店还冒着热气,路上的车却已经不少了。爸爸开车,妈妈坐副驾,一路上话都比平时少些。
那所学校比陈紫昂想象中大。
门口的石碑擦得很亮,校门里是一条笔直的主路,两边树种得很整齐,教学楼浅灰色,玻璃窗在晨光里泛着白。校门口已经站了不少家长和孩子,有人低头看资料,有人最后翻一眼笔记,有人看上去很轻松,像来参加一场再平常不过的练习。
陈紫昂站在人群里,第一次很鲜明地感觉到自己已经不在“三年级那个小池子里”了。
这里很多孩子都比他高一点,气质也老练一些。有人抱着文件夹,里面夹着厚厚几页打印题;有人小声和同伴讨论“这类程序阅读一般会不会考递归调用次数”;还有人说到“如果机试碰到线性 DP 套路,先拿稳再看后面”。
那些话从他耳边飘过,像一阵一阵的风。
他听得懂大半,也正因为听得懂,心里更发紧。
原来大家真的是带着东西来的。
不是来碰碰运气,不是来试一试,而是真的能写、能算、能分析。
妈妈替他理了理衣领,低声说:“别看别人。把你会的写出来就行。”
陈紫昂点点头,可他知道,真正坐进考场以后,“别看别人”这句话其实很难做到。因为那种压力不是你去看谁,而是你会清楚地感觉到,周围每个人都在和你一起往前跑。
秋令营先考的是笔试。
九十分钟,五道综合分析题。
卷子发下来时,纸张摩擦出“哗”的一声,整个教室瞬间安静了。阳光从窗边斜着落进来,照在每个人的卷面上,显得那一行行程序格外黑。
陈紫昂低头一看,心里先轻轻一沉。
不是那种他最熟悉的“给题面,写程序”,而是程序分析。给你代码、给你数据范围,要你去算复杂度,去分析某个函数会被调用多少次,去推程序最后输出什么,甚至去判断某段程序在某些特殊数据下会发生什么。
这东西比写题更“干”。
它要求的不是熟练敲代码的手,而是能不能把程序在脑子里跑起来。
第一题是复杂度分析。
一个双层循环,里面套着一个不断除二的 while。如果只是看表面,很容易写成 O(n^2);可陈紫昂盯着那段代码,心里隐约觉得不对。最里层那个变量每次都减半,不是线性往下走,而是“砍一半,砍一半”地掉。小周老师暑假讲过类似的例子——这样的部分,复杂度往往带着一个 log。
他在草稿纸上写了几组小数据,1、2、4、8,变量走了几次,心里慢慢稳下来。
是 O(n log n)。
他把答案写上去时,心里有一瞬间很轻的亮。
第二题更麻烦,是一个递归函数,问在输入 n 的时候,某个辅助函数总共会被调用多少次。
函数长得有点像斐波那契,f(n) 要调 f(n-1) 和 f(n-2),但又在某个位置多嵌了一个判断。卷子上的代码冷冰冰地摆在那里,一眼看过去,像一团纠在一起的树枝。
陈紫昂最开始也有点乱。
可他没急着直接算,而是先在草稿纸上画调用树。f(4) 会调出什么,f(5) 又会多出哪些分支,他一层一层往下画。草稿纸上的树枝越长越多,像冬天窗玻璃上的冰花,一点点往外长。画到一半时,他忽然看出规律了:这其实不是要你真的把所有调用写完,而是看你能不能把“次数”本身也当成一个递推来想。
那一下的感觉很奇妙。
像原本一团乱麻,忽然被你找到了一根可以一直往外抽的线。
第三题考输出。
一段不长的程序,里面有数组,有交换,有条件跳转,最后问程序打印什么。表面上不算吓人,可藏着两处非常容易看漏的小细节:一次变量自增的位置,一次交换后下标的变化。陈紫昂拿铅笔,一步一步在边上模拟,写出每一步数组的样子。那过程很像他以前做最基础模拟题时的样子,只不过对象从“题目规则”变成了“代码本身”。
第四题开始难了。
是一道关于循环次数精确计算的题。程序里套了几层并不完全规则的循环,问某个函数会被调用几次,不是估一个数量级,而是要算出精确值。陈紫昂看着卷面,脑子里很快浮出“等差求和”“分情况”的影子,可题里有一个下取整,让整个式子忽然卡住了。
他试着代入小数据,想从具体数里摸规律。
1,2,3,4,5……
写着写着,他发现规律是有,可远没他想得那么干净。尤其到中间某一块时,数据会跳,不是简单线性过去。他咬着嘴唇,盯着那几行数字,心里一点点发热。教室里太安静了,只有笔尖摩擦纸面的细声,还有远处偶尔有人轻轻翻页。
他想了很久,最后还是没能把那道题彻底算透。
第五题则更像一道“混合题”:既要看懂程序在做什么,又要分析它在某些输入下的行为,还带一点边界讨论。到了这时候,时间已经被前几题吃掉不少,陈紫昂明显觉得后劲有些不够。他硬着头皮把能写的先写出来,把自己有把握的结论先落下,不确定的地方则留了一点空白。
铃声响起的时候,他放下笔,指尖都有些发麻。
走出考场去吃午饭那会儿,校园里的风把树叶吹得哗哗响。家长不能进教学楼,他只能跟着其他孩子一起往食堂那边走。秋日的阳光照在砖地上,很亮。他端着餐盘坐下,旁边几个孩子已经开始低声对答案。
“第二题我觉得是递推,不然手推推不出来。”
“第四题那个下取整很烦,我最后也没算干净。”
“第一题要是写成 n^2 就亏死了。”
陈紫昂低头扒饭,心里却慢慢松了一点。
原来不只是自己会在某些地方卡住。
这感觉不算宽慰,却至少让他知道:这场考试确实不轻。
下午出分很快。
89 分。
不算低,甚至已经挺好。可他自己也知道,那些真正难、真正拉开层次的计算题,他还是没能完全啃下来。
这就是他和“特别出色”之间的第一道缝。
不是不会,只是到了更深一点的地方,还是差一点狠劲和积累。
真正让他觉得“开始打仗了”的,是下午那场三小时机试。
机房很大,一排排电脑整齐地亮着。空调有些凉,显示器的光把每张脸都照得发白。监考老师念规则时,陈紫昂手心微微出汗,鼠标边上那张草稿纸被他按出一小片折痕。
三小时,六道题。
赛时没人会告诉你难度标尺,只有做完以后,大家才慢慢从讨论和结果里推断:第一题大约只是入门,第二题在普及-,第三第四题已经到了普及/提高-,后面两题则明显高了一个台阶,差不多是普及+/提高的难度。
但坐在那一刻,他不知道这些。
他只知道,自己面前是一整排陌生题目。
第一题很规矩。
一个典型的模拟小题,读题、照着做、细心别漏情况。他看完以后心里反而安定下来,像刚进水的人终于摸到一块不滑的石头。写代码,跑样例,交上去,Accepted。
绿色跳出来的时候,他几乎觉得肩膀轻了一点。
第二题是枚举里带一点小优化。
题目表面看很普通,问在若干限制下有多少种方案。如果直接四层循环,会炸;但条件里有一个范围约束,能让你提前剪掉很多不可能情况。陈紫昂盯着题面看了一会儿,脑子里慢慢冒出暑假刷过的一类题:不是盲目枚举,而是先固定两层,再根据剩下条件去缩范围。
他在草稿纸上列了几个小样例,确认自己的思路没偏,便开始写。这个过程不算轻松,中间还因为一个边界条件改了一次,但最后还是过了。
两题到手,他心里已经有了点底。
第三题开始,题面明显变冷。
给了一串数据,要你在某种限制下找最优值。直接做会慢得离谱。陈紫昂看了几遍,先是想到前缀和,又觉得不够;再看一会儿,忽然嗅出一点“二分答案”的味道。因为答案越大,要求越苛刻,似乎存在某种单调性。
这是他暑假综合训练里刚碰熟的一类东西。
他没有立刻写代码,而是先在纸上写:能不能二分?判定怎么做?
然后一点点试小数据。
如果答案取成 x,那每一段要满足什么?怎样贪心地扫过去,才能判断“当前这个 x 能不能实现”?他在纸上画了几段线,标了几个点,越画越顺,心里那条线也慢慢亮起来。
这道题他写得并不快。
可当他把判定函数敲出来、再把二分框架套上去的时候,整个人却有一种奇异的镇定——不是撞大运,而是真的知道自己为什么这样写。
提交,Accepted。
第三道绿出来那一下,他的心跳得很快。
不是因为三题多厉害,而是因为这是他第一次在真正的实战里,靠自己把一类“有算法思想”的题独立拧开。
第四题更让他高兴。
那是一道线性 DP 套路题。
题面讲的是一串位置、若干代价和某种连续约束,外表看起来并不显山露水,但如果没有接触过这类状态设计,很容易从头就走偏。陈紫昂刚看到时,也先愣了一下。可暑假里那些在本子上写满 f[i] 的下午忽然回来了——状态是什么意思,从哪里转移过来,当前这一格最优值应该怎么继承前面的信息。
他在草稿纸上写了几项:
f[1] = ...
f[2] = ...
f[i] = ?
一开始有点卡,后来某个转移忽然顺了。像拉拉链时卡住的齿,轻轻一带,突然就全合上了。
他把代码敲完,自己拿样例跑了两遍,再交。
又是绿。
那一刻,他是真的有点想笑。
不是那种大声张扬的笑,而是嘴角很轻地往上一挑,眼睛也跟着亮了一点。因为他心里忽然很清楚:如果五月那场三年级排名赛把第五题再摆到他面前,他现在大概真能做出来了。
可比赛并不会因为你前四题顺,就自动把后面两题也交给你。
第五题一打开,气氛立刻就变了。
题目不长,但条件密,带着很明显的“不是套路题”的味道。它不是简单问最优值,也不是标准结构上套个模板就能解。陈紫昂盯着屏幕,先试图从范围和性质入手,看是不是某种贪心、某种 DP 变形,可越看越觉得不稳。题里像是藏着一个关键转化,只有把那层纸捅破,后面才会一路顺;可现在的他,显然还碰不到那层最关键的地方。
他不甘心,硬着头皮写了点暴力和部分分。
第六题更冷。
图或者树的影子一露出来,他心里就先凉了半截。题目像一片枝杈很多的森林,你站在入口能看见里面有路,却根本不知道该从哪根树枝开始爬。机房里安静得只剩下敲键盘声,有人显然已经在第五第六题上推进了,键盘敲得又快又稳;而他盯着题面,感觉自己脑子里像起了一层薄雾。
他知道,自己该做的不是死熬。
可真正面对一道“你就是暂时不会”的题时,那种无力感,还是会一点一点漫上来。
最后几十分钟,他在第五第六题之间来回切换,能捞的分尽量捞,能写的性质尽量写,直到结束铃响起。
屏幕定住的时候,他整个人都僵了一下。
四题。
稳稳四题。
比他最开始担心的好,可也远没有好到“惊艳”。
成绩是在当天傍晚汇总出来的。
教学楼走廊里人很多,家长在外面等,孩子们三三两两站着,神情都不太一样。有的明显很轻松,和同伴说笑;有的低头不语,手里捏着准考证;还有的正快速回忆某道题,像想把已经结束的比赛再追回一点什么。
陈紫昂站在人群里,手心凉凉的。
最后排名出来时,他先是愣了一下。
笔试加机试,总排前 10%。
一等优秀。
这已经非常不错了。
对于一个真正意义上第一次参加这种层次秋令营的孩子来说,这个成绩足够说明很多问题:底子扎实,脑子灵,实战也不算怯场,至少不是来陪跑的。
妈妈听见结果,眼睛一下子亮了:“前 10%?很好啊!”
爸爸也难得露出很明显的笑:“不错,真不错。”
可陈紫昂自己心里却并没有完全飘起来。
因为他知道,前 10% 和“特别出色”之间,还是隔着一层。
前面那批真正惹眼的孩子,可能是笔试近满,机试五题甚至更多,整个人在考场里像已经练过很多很多次那样老到。他四题,一等优秀,当然值得高兴;可还没有高到足够让所有人的视线一下子聚过来。
这就是那种最复杂的成绩。
你不能说它不亮,可它也没有亮到刺眼。
它像一块已经磨出光的石头,握在手里沉甸甸的,可还没到那种一看就知道是宝物的程度。
回家路上,车窗外的天慢慢暗下来。街灯一盏一盏亮起,城市像刚从白天里退出来,边缘都柔和了一些。妈妈还在回味那个名次,语气里带着真心实意的欣慰:“前 10%,说明你在四年级里也不算靠后了。”
“嗯。”陈紫昂点点头。
爸爸开着车,过了一会儿才说:“说明这暑假没白练。但也看出来了,最前面那一层,还是很难。”
这句话一出来,车里安静了几秒。
陈紫昂看着窗外掠过的一片片灯影,轻轻“嗯”了一声。
他没有觉得被打击。
因为爸爸说的是事实,而他今天第一次这么清楚地摸到了这个事实的边。
真正的后续,是几天之后来的。
九月底,他获得了参加 CSP-X 的机会。
这是山东全省范围的小学生联考,规格更大,题目也会更难,和这次秋令营类似,但明显再往上抬了一个层级。能拿到参赛机会,本身已经算是秋令营成绩带来的直接回报。
消息传来那天,妈妈在客厅里高兴得来回走了两步,爸爸也立刻问时间安排。陈紫昂则坐在书桌前,听着“全省联考”“题会更难”“是个很好的见识机会”这些话,心里竟然比秋令营前平静一些。
因为秋令营已经让他知道,自己大概站在哪里。
不算底下,甚至还可以说靠前。
但离真正拔尖,还有距离。
这份“知道”有点冷,可也很珍贵。它让人不再瞎猜,不再把自己想得太弱,也不再把自己看得太强。
晚上,妈妈问他:“紧张吗?”
陈紫昂想了想,说:“有点。”
“怕考不好?”
他摇了摇头,又点了点头,最后自己都笑了一下:“我感觉……去省里应该就是当炮灰的。”
妈妈本来想安慰,听见“炮灰”两个字,反而先笑出了声:“你这词跟谁学的?”
“群里有人这么说。”他老老实实回答。
妈妈笑完以后,还是认真了些,走过来摸了摸他的头:“见高手也不是坏事。能当‘炮灰’,至少说明你已经进到炮火范围里了。”
这话说得有点好笑,也有点真。
陈紫昂低头看着桌上的笔记本,忽然觉得心里没那么沉了。
是啊,他已经进到炮火范围里了。
半年前,他还只是刚刚学会循环、数组和最基础模拟的小孩;暑假里,他在风扇底下写 f[i],在草稿纸上画二分判定,到了现在,他已经能在一场真正的秋令营里稳稳做出四道题,站进前 10%。
虽然还远远不够,虽然还要被更厉害的人压着打,虽然去省联考时大概率真的只是去见世面、当背景板——可那又怎么样呢?
有些人就是先做背景板,才慢慢学会站到前面。
那天夜里,他回到书房,重新打开洛谷。
页面还是熟悉的页面,题单、记录、讨论、榜单,一切都安安静静地排在那里。可他坐在椅子上,却觉得自己和第一次注册账号时已经完全不同了。那时候他是站在城门外往里看;现在,他已经在城里跑过一段路,见过高墙,见过快马,也见过自己在真正比赛里是怎么喘、怎么稳、又怎么被卡住的。
他翻开笔记本,在一页空白处写下:
秋令营:89 分,四题,前 10%。
CSP-X:去见识。
写完以后,他盯着那几个字看了一会儿。
窗外风吹过树叶,声音轻轻的。九月快走到头了,空气里已经有一点真正秋天的凉意。对面的楼还有几扇窗亮着灯,像远远近近的一些目光。
陈紫昂坐在自己的灯下,忽然很清楚地知道,接下来那场全省联考大概会很难,难到他得做好“看别人做第五第六题”的准备,得做好自己只能拼前几题、后面去捞边角分的准备,甚至得做好真正认识到“天外有天”的准备。
可他也第一次不那么怕这种“知道自己会被打”。
因为秋令营已经告诉他一件事:
他不是不能打。
他只是还不够强。
而这两者之间,差的不是天生就绝对过不去的鸿沟。至少现在看,还不是。
他伸手把笔帽扣上,动作很轻。
书房里只有台灯暖黄的光,落在他的睫毛上,也落在那本密密麻麻写满了题目、方法、得失的小本子上。一个更大的考场正在前面等着他,里面会有更多更早开始学的孩子,会有更冷的题面,更高的门槛,更真实的落差。
他知道。
可他还是想去。
哪怕真的是去当一次炮灰。
第六章:第一次被打疼
九月的后半截,济南的天开始一天比一天高。
风从校门口吹进来,已经带了一点干净的凉意。梧桐叶在阳光里翻着浅黄,操场边的草也不像盛夏时那么发亮了。可陈紫昂的日子却一点没有松下来,反而像被一根线越拽越紧。
因为 CSP-X 要来了。
这是山东省范围的小学生联考,先初赛,再复赛。和秋令营不一样,这一次不只是某一所学校里的孩子来碰一碰,而是整个省里真正想在信息学这条路上走下去的孩子,都会往这里挤。
陈紫昂知道,自己大概率是去见世面的。
可“去见世面”这件事,一旦真的落到日历上,还是会让人心跳快一点。
初赛在九月底。
这段时间,小周老师讲课的方式都变了。以前更多是在讲知识点、讲题型,到了这会儿,则明显开始朝着“考试里的坑”去。理论题怎么排除,程序分析题怎么手推,完善程序题怎么猜变量含义、怎么从上下文补代码。
“初赛不是写程序。”小周老师站在白板前说,“它考你脑子里有没有东西,考你细不细,稳不稳。很多题不是不会,是算错、看漏、或者被代码样子吓住了。”
陈紫昂坐在第三排,拿着笔,一行一行记。
初赛的题型他之前已经听说过很多次了,但真正开始系统准备,还是第一次。
二十个选择题,二十分。
五个程序分析题,五十分。
三个完善程序题,三十分。
满分一百。
秋令营的笔试已经让他知道,程序分析绝不是随便看看就能蒙过去的。你得真正在脑子里把代码跑起来,得看得出循环里藏着什么,递归里会长出什么,复杂度到底是线性的、平方的,还是突然拐出一个 log。
而理论题对他来说,则像另一种陌生的硬东西。
比如有一道训练题,给你一棵二叉树的前序和中序遍历,让你选后序遍历。小周老师在白板上写:
前序:A B D E C F
中序:D B E A C F
然后转身问:“根是谁?”
“前序第一个。”前排一个孩子立刻答。
“对。那根是 A。中序里 A 左边是谁,右边是谁?”
左边是 D B E,右边是 C F。
“再往下拆。左子树的根是谁?右子树的根是谁?”
陈紫昂盯着那两行字,脑子里慢慢把树长了出来。先找根,再分左右,再递下去。最后树一搭好,后序也就出来了:左、右、根。
这种题刚开始做的时候,他总觉得像在雾里摸东西。不是不会,而是慢。别的孩子看一眼就能在脑子里转完,他还得在草稿纸上画树杈,一层一层标。
还有一种题是“选出哪个序列可以二分”。
题目给四组看似差不多的数列,让你判断哪个满足某种单调性、哪个可以通过二分答案去做。这样的题最烦,因为它不问你代码,只问你脑子里有没有“那个味道”。有时候四个选项都像能二分,又都像不太对,你得靠自己一点点剥。
陈紫昂做这种题时,常常会在纸上写“小样例”。
如果答案取成 5,会怎样?取成 6,又会怎样?
是不是越大越难?是不是越小越松?
有没有一条清楚的分界线?
写着写着,有时候他能摸到;有时候摸不着,就只能凭感觉排掉两个最不靠谱的,再赌一个。
至于完善程序题,对他来说又是另一种折磨。
给你一段现成代码,但写得很烂,变量名简短得像随手乱敲的,缩进也不整齐,逻辑还故意弯来绕去。每道题有五个空,要你把缺失的代码填进去。前两个空还可能好猜,到后面就很像在一团毛线里找断头。
小周老师给他练了一道简单版的完善程序,是用双指针求最短区间的。
代码里变量名只有 l, r, s, ans,中间空了五处。陈紫昂坐在电脑前,先把整段程序从头到尾读一遍,再在旁边小声念:“s 应该是当前区间和……那这里肯定要加 a[r]……既然 while 里面在缩左边,那这里大概是 s -= a[l],然后 l++……”
他说得很慢,像一个小孩在夜里摸着黑拆钟表。
有一回做到第三题,那代码是在图上做点什么,变量名全是单字母,函数一层套一层。他看了十分钟,只觉得满眼都是刺。
“老师,这谁看得懂啊?”他忍不住问。
小周老师笑了一下:“初赛出题人就想看你看不看得懂。”
“可这写得也太丑了。”
“比赛不会因为写得丑就让你少丢分。”小周老师说,“看不懂,也得想办法看。”
这句话把陈紫昂堵得没话说。
他只好低头,继续一行一行往下抠。
越临近比赛,他心里那种“我是去当炮灰的”的感觉反而越具体了。因为练得越多,越能看出来这套初赛卷子对高年级学生到底有多友好。很多五六年级的孩子,早就练过好几年程序分析、完善程序,甚至有些人做这种题已经形成手感,看到一段递归代码,脑子里调用树会自己长出来;看到一段烂代码,先猜它想干什么,再去填空。
而他呢?
他连“这个烂代码想干什么”有时都要多看三遍。
可他也在用一种很笨、很硬的方式往前补。
每天晚上,书桌上的灯总要亮很久。台灯照着他的额头,照着铺开的草稿纸,照着那本深蓝色笔记本里越来越密的字:
“前序第一个是根”
“二分要有单调性”
“完善程序先猜函数作用”
“复杂度看最里层怎么变”
九月底前的最后一个周末,妈妈推门进来时,看见他正趴在桌上,拿铅笔一点一点地推一段递归程序。纸上全是树杈和箭头,像一片小小的森林。
“还没做完?”妈妈轻声问。
陈紫昂头也没抬:“快了。”
“紧张吗?”
“……有点。”他停了一下,又补了一句,“感觉会被打得挺惨。”
妈妈想笑,又没笑出来,只是伸手摸了摸他的后脑勺:“那就去看看,别人怎么把你打惨的。”
陈紫昂也笑了。
笑完以后,还是继续低头推程序。
初赛那天,天有些阴。
考场设在一所小学里,教室外的走廊很长,地砖被拖得很亮。门口站着不少家长和孩子,空气里有一种很明显的安静,不是没人说话,而是每个人说话都压着点声音。
陈紫昂跟着队伍走进教室的时候,心口跳得很快。
两小时,一百分钟的理论与分析。
卷子发下来,他先翻了一遍,指尖就凉了一下。
前面的二十道选择题果然很杂。二叉树遍历、排序稳定性、能否二分、简单图论判断、复杂度直觉……像一把小锤子,一下下敲你脑子里到底装了多少东西。
第一道是二叉树遍历。
给了一组前序、一组中序,让选后序。这个他练过,先找根,再拆左右。他在草稿纸上飞快画树,最后把答案圈上,心里稍微稳了一点。
后面有一道问四个序列里哪个适合二分。
选项看起来都像模像样。陈紫昂盯着那四组数,眉头一点点皱起来。他在边上写了几个小情况,代入、划掉、再代入。最后排掉两个最不可能的,再在剩下两个里反复看,才勉强定下来。
这样的题一多,二十题做完,他已经觉得脑子有点热。
真正难的是中间那五道程序分析题。
第一道还好,是一道复杂度分析。三层结构里最里层是一个不断减半的循环,他看了几眼,知道不是纯平方,应该带个 log。这种基础题,他反而不太怕。
第二道开始麻烦,是一段递归程序,要求算某个函数一共会调用多少次。代码里有两个分支,还掺了一个判断。陈紫昂第一眼看下去,只觉得像有人把一团线揉成了球塞进卷子里。他深吸一口气,在草稿纸上从 n=1、2、3 往上推,慢慢数出调用次数,再从数字里摸规律。
他最后摸到一半,心里有七成把握,却不敢说十成。
第三道是求输出。变量交换、数组下标、循环嵌套,有两个地方特别容易看漏。陈紫昂几乎是咬着牙,一步一步写状态,生怕漏掉一个自增、自减。
第四道开始,题目突然变刁。
要求精确算某段程序执行了多少次,不只是估复杂度,而是要真的推出式子。中间带一个下取整,还有一次分情况讨论。陈紫昂做着做着,就明显感觉自己跟不上了。他能保证基础部分不出错,中间难一点的地方则像踩在滑石头上,刚觉得站稳,又滑一下。
第五道更狠。
程序不长,但逻辑很拧,要看出它本质上在干什么,再判断特殊输入下会怎样。陈紫昂读了几遍,脑子里只剩下一个感觉:这不是给我这种刚升四年级的孩子准备的。
他还是尽量写,把自己确定的部分先写上,不确定的留白少一点,猜测多一点。
做到这里,他心里已经很清楚了:程序分析这块,自己最多就是基础不崩,中等题看运气,高难题只能蒙。
后面的三道完善程序题,则又是另一场硬仗。
第一题偏基础,是个简单枚举加判断。代码写得乱,但意图还算明显。陈紫昂先看主循环,再看变量含义,五个空慢慢往里填。填完以后,他甚至还反过来从头读了一遍,想确认逻辑没断。
第二题稍微麻烦些,像是某种线性 DP 或前缀统计。变量名字都短得吓人,缩进也不工整。可好在整体结构还能猜。陈紫昂一边看,一边在边上写:“这里应该初始化”“这里取最小”“这里更新前缀”。写着写着,居然也大体补上了。
第三题一出来,他是真的有点懵。
代码本身就烂,算法也不是他熟的路数。变量来回绕,函数套函数,像一条进了山又钻洞的路。他看了五分钟,只觉得每个字都认识,连成一段却像天书。
他试着先猜最外层在干嘛,猜不准;再看里面的循环想补一空,也不稳。到最后,只能靠上下文和一点点语感去填。
铃声响的时候,他放下笔,先觉得眼睛有点酸。
走出教室,天还是阴的,风比早上更凉了一点。外面家长在等,走廊里孩子们已经开始小声对答案。
“第三个完善程序你看懂了吗?”
“没完全懂,我后两个空都是猜的。”
“第四个程序分析那个次数我感觉要分类讨论。”
“选择题那个能二分的我选 C。”
这些话一阵阵飘过来,像风吹着碎纸。
妈妈迎上来问:“怎么样?”
陈紫昂想了想,说:“基础题还行,难的……有点乱。”
“能进复赛吗?”
他低头看着鞋尖,很诚实地说:“不知道。”
不知道,是那几天最准确的状态。
初赛不像机试,考完出来大概能知道自己做出几题;这种卷子,选择题有时就是一念之差,程序分析差一步就是整道没了,完善程序更是填错一个空就可能连锁崩掉。
他回家以后,什么都没法确定。
小周老师在群里大致讲了讲题型,说今年初赛不算简单,尤其程序分析和第三道完善程序,区分度很大。妈妈听完,心里反而更紧。因为“不简单”这种话,对所有人都成立,并不自动等于“紫昂有利”。
那几天晚上,陈紫昂还是照常写题,可明显有点浮。
有时写到一半,脑子忽然飘回考场:那道递归次数题自己最后写的到底对不对?完善程序第三题第一个空是不是该写成另一个条件?那道能不能二分的选择题,自己是不是被假单调骗了?
这些念头像小刺一样,时不时冒出来扎他一下。
他知道,考都考完了,再想也没用。
可一个小孩真正等成绩的时候,哪有那么容易“不想”。
妈妈比他更容易焦躁。
手机消息一响,她就下意识拿起来看;家长群里只要有人说“听说快出了”“有人估分了”“某机构老师觉得线大概是多少”,她就会认真看半天。爸爸表面上平静些,可晚上吃饭时,也会随口问一句:“今天有消息吗?”
没有。
一直没有。
这种等榜的日子很怪。表面上家里还是照常运转,早上上学,晚上吃饭,做题,睡觉;可空气里总像浮着一层看不见的薄膜,每个人说话都绕着那个结果。
有天晚上,陈紫昂写完一道枚举题,关掉页面,忽然问妈妈:“要是没进复赛,会不会特别丢人?”
妈妈正在洗水果,手上还带着水珠。她停了一下,回头看他:“为什么会丢人?”
“因为秋令营都前 10% 了,要是初赛都没过……”
妈妈走过来,把水果盘放到桌上,看着他:“过不过,都只是说明你现在到哪。又不是说明你行不行。”
这话他说不上来有没有被完全安慰到。
但那晚他还是多吃了两块梨。
成绩出来是在一个傍晚。
那天放学回来,天边有一点很淡的晚霞。陈紫昂刚把书包放下,妈妈的手机忽然震个不停。家长群、机构群、朋友转发,几乎同时刷了消息。
CSP-X 初赛成绩开放查询。
书房灯一下子亮了。
妈妈坐到电脑前时,手指都比平常快。陈紫昂站在旁边,背挺得笔直,手却不自觉抓住了裤边。网页刷开的那几秒,房间里安静得只有鼠标轻轻点动的声音。
然后分数跳出来:
65 分。
陈紫昂先是愣了一下。
妈妈立刻去看分项:
选择题 15 分。
程序分析 30 分。
完善程序 20 分。
一共 65。
“线是多少?”爸爸从门口问。
妈妈赶紧翻群消息,心跳得都快了:“63……今年入围复赛线是 63!”
书房里先静了一秒,接着妈妈一下子笑了出来:“进了!紫昂,你进复赛了!”
陈紫昂还是有点没反应过来。
不是因为分高,恰恰是因为分不高。
65,复赛线 63。
不是稳稳当当越过去,而是像踮着脚,险险从门槛上迈过去。可不管怎么说,他毕竟迈过去了。
那一刻,他心里猛地涌上一股很奇怪的感觉,像原本以为自己会被拦在门外,结果门居然真的开了一条缝。
妈妈高兴得在屋里转了一圈:“太好了,太好了,至少见到复赛了。”
爸爸也笑了,难得语气里有点明显的松快:“行,这次算真见到大场面了。”
陈紫昂低头又看了一眼分数,心里慢慢才浮上真正的惊喜。
65 分,不是特别亮眼,甚至可以说非常惊险。可对他来说,这已经够像一张入场券了——不是证明他有多强,而是证明他至少没被第一轮就打回去。
他忽然想起考场上那一整排自己觉得很悬的程序分析和第三道完善程序,想起那几天等榜时心里一直悬着的小石头,忽然觉得胸口都轻了。
那晚妈妈多炒了一个菜,爸爸还开玩笑说:“这回真得去当炮灰了,省里的正式炮灰。”
陈紫昂笑出了声。
笑过以后,他又有点安静下来。
因为他知道,复赛和初赛完全不是一回事。
初赛是站在门口看门。复赛,是进去挨打。
复赛在十月上旬,国庆假期里。
时间突然一下变得很紧。
小周老师知道他是卡线进的复赛,第一句话就说得很实在:“先别想拿多高名次,目标就是把会的东西尽量写出来,别因为紧张把前面的分丢了。”
复赛是 3.5 小时,六道编程题。
不再有那种“纯送分”的入门题。
也就是说,从第一题开始,你就得真正写程序、真正拆题、真正面对不会。
这对陈紫昂来说,比初赛可怕得多。
小周老师给他做了几次模拟,专门按“没有送分题”的节奏来。第一题就是一个要写三四十行的模拟,第二题开始就不再温柔。有的题明明考点是他学过的,可稍微换个壳,他就会一下子摸不着;有的题则明显已经超出了他当前的舒适区,需要观察性质、需要灵光一闪、需要更厚一点的积累。
有天晚上,小周老师给他讲一道“根据性质去二分”的题。
题目乍看根本不像二分,甚至看上去有点像贪心。可老师在白板上推着推着,忽然指出一个关键性质:如果某个答案可行,那么比它更松的答案也一定可行;如果某个答案不可行,那么更严的答案也一定不行。
“看见没有,这就是单调性。”
陈紫昂盯着白板,听明白了,却又觉得这种明白有点虚。因为这种题最难的根本不是“知道二分需要单调性”,而是你得在一堆乱糟糟的条件里,自己把那条单调线找出来。
这不是背模板能解决的。
还有一道数论题,小周老师讲到线性筛、质因数分解、再往后接贪心和二分。讲完以后,陈紫昂看着笔记本上的整页推导,只觉得自己像刚跟着老师翻过一座山,回头一看,连第一步踩在哪块石头上都快忘了。
“这种题你现在看个感觉就行。”小周老师说,“真到赛场上,不一定写得出来。”
这话说得很平淡,却也很诚实。
陈紫昂点点头。
他知道老师不是在泼冷水,而是在帮他把位置摆正。
国庆前的最后几天,他练得很用力,却也越来越清楚:复赛大概率会把自己打得很惨。尤其是想到那里面会有五六年级的孩子,会有比自己早练两年、三年的人,会有已经能把很多套路和观察揉在一起写的人,他心里那种“我是炮灰”的感觉又慢慢回来了。
但和初赛前不一样,这次的“炮灰感”里,多了一点硬。
像你明知道前面是墙,却还是想上去撞一撞,看看自己到底会被弹回来多远。
复赛当天,天很晴。
国庆假期的校园比平时安静,可机房外的人一点不少。秋天的太阳照在玻璃上,亮得发白。陈紫昂走进机房时,手心还是出汗,可心里反而没有初赛那么乱。
因为他已经知道了:自己就是来被打的。
既然如此,就先把能打回去的地方打回去。
3.5 小时,六道题。
卷子一打开,他先扫了一眼,心里立刻一沉。
果然,没有送分题。
第一题就是模拟,而且不是那种几行循环就能解决的小模拟,而是一个需要把流程、状态、边界都理得清清楚楚的题。光读题就花了不少时间。题目里有几种操作,某些条件下要切换状态,还有一处特别容易漏掉的重置。
陈紫昂咬着嘴唇,把草稿纸分成几块,先画流程图,再写几个样例,一步一步推。等他真正开始敲代码时,已经不剩多少从容了。
这题他写了三十多行,改了两次边界,样例终于过。
提交,Accepted。
第一道绿出来的时候,他心里是松了一口大气的。因为这种题如果第一下就崩,后面很容易整个人都乱。
第二题是一道贪心。
题目讲的是若干对象在限制下如何安排,问怎么选才能最优。表面上条件不少,可陈紫昂看着看着,忽然想起暑假里一类很像的题:先排序,再每一步尽量做当前不吃亏的选择,给后面留空间。
“不会吃亏。”
这四个字像小周老师在白板前说话的声音,一下子从脑子里浮出来。
他迅速列了几个小样例,验证自己的策略没问题,就开始写。排序、遍历、维护当前答案,代码并不花哨,却写得很顺。交上去,又是一道绿。
两题一百加一百。
如果停在这里,这场复赛甚至会给人一种“好像还能打”的错觉。
第三题是线性 DP。
题目本身陈紫昂其实并不陌生,状态设计也不算完全没思路。可写到一半,他就卡住了。因为最朴素的转移会超时,明显需要优化。而这种优化不是简单压个数组、少一层循环就行,而是要用前缀和去提前维护某些量,让每次转移快下来。
陈紫昂没有学过这种优化。
他看着题面和数据范围,先是试着按最原始的 DP 写,写完自己都知道不行。接着他去看能不能硬剪枝、能不能从转移式里省掉点东西,可越看越没头绪。那种感觉很憋——你知道题目主体自己摸到了,可真正决定这题能不能过的那一步,你完全不会。
机房里冷气很足,他却觉得后背慢慢发热。
最后他只能退一步,写了个朴素版本,能拿多少部分分算多少。
提交以后,他没敢多看评测结果,直接去看第四题。
第四题更让人难受。
题目看起来不像任何他熟悉的模板。需要先观察出一个性质,再根据那个性质去二分。不是“知道有单调性”就够,而是你得先看出某个隐藏很深的结论,才会明白为什么能二分。
陈紫昂盯着屏幕,感觉像在看一面光滑的墙。
他知道墙后面大概有门,可自己根本摸不到门把手。
他试着列小样例,试着找规律,可样例规模一大,规律又像水面上的影子,一碰就散。时间一分一分过去,旁边不远处有人敲键盘敲得很快,他却连“第一步到底该往哪儿想”都没抓住。
这题他最后只能放弃。
第五题是数论加二分加贪心。
题面刚开始那部分,他其实还有点感觉。看到数据范围和某些关于因子的描述时,他第一反应是:要先线性筛质数,再做质因数分解。
这一步他会。
他甚至把筛法和分解部分写得挺顺,像终于在黑屋子里摸到一块熟悉的砖。可再往后,题目真正想要的不是“分解完就结束”,而是你得利用这些分解结果,再去构造一种判定,再套二分和贪心。
这部分对他来说,完全像进了另一层楼。
他能感觉到题目后面有路,可那路怎么接、怎么走、为什么这样贪,他一点不稳。最后只能写个暴力,再加上一点自己猜的优化,能骗到部分分就骗一点。
第六题是树形动态规划。
光看到“树”这个字,他心里就先凉了一下。再看题目,果然更凉。状态在树上转移,显然不是他现在能完整啃下来的东西。他硬着头皮读题,先看有没有什么简单性质,结果发现当树退化成一条链时,问题会简单很多。
这至少是一块能捞的分。
他立刻抓住这条链的部分分写。把一般树简化成线性结构,再做一维 DP。这一部分他反而写得很认真,像在一片被大火烧过的地上,拼命把还能收回来的几粒种子捡起来。
考试结束的时候,他整个人都有点空。
前两题满分。
第三题部分分。
第四题零。
第五题部分分。
第六题链的部分分。
他不用等结果,心里就已经大概有数了。
这不是“发挥不好”。
这就是“水平还没到”。
走出机房时,太阳还是很亮,校园里却有一种特别空的安静。家长们在外面等,脸上都带着那种想问又不敢立刻问的神情。
妈妈一看见他就迎上来:“怎么样?”
陈紫昂背着书包,沉默了两秒,才说:“前两题全做出来了。后面……被打得挺惨。”
妈妈没继续追问,只是点点头:“先回家。”
回家路上,车里很安静。
窗外国庆的街道挂着旗子,商场门口人来人往,世界显得很热闹。可他坐在后座上,只觉得心里很空。那种空不是因为自己一无是处,而是因为他第一次这么清楚地被题目按在地上,知道自己会一点什么,也知道自己不会的地方有多大一片。
成绩出来的时候,比他预想得还要具体、还要刺一下。
100 + 100 + 30 + 0 + 30 + 30 = 290。
总分 290。
二等奖分数线是 300。
差 10 分。
最后只拿到了一个 三等奖。
当那串数字真正摆到屏幕上的时候,陈紫昂先愣了一下,接着胸口像被什么东西轻轻撞空了。
不是因为三等奖特别差。
放在全省联考里,一个刚升四年级的孩子,卡线进复赛,最后还能靠前两题和几道部分分拿到 290,已经不算难看了。甚至可以说,已经很能说明问题了。
可问题就在于——
二等奖线是 300。
只差 10 分。
就像一道门,他明明已经摸到了门框,手指甚至都能碰到那块木头了,却还是没能跨过去。
妈妈看见成绩,第一反应是安慰:“差 10 分,说明已经很接近了。”
爸爸也说:“前两题满分,后面还拿到不少部分分,不丢人。”
这些话都是真的。
可陈紫昂坐在书桌前,看着那串数字,心里还是一点一点酸起来。他不是不懂事,也不是一定要拿多高的奖才肯罢休。可正因为他看得懂这些分怎么来的,才会更清楚地意识到:自己输的不是运气,而是实打实的那一层水平。
第三题,他会 DP,但不会前缀和优化。
第四题,他连门都没看见。
第五题,他会线性筛,会分解,可后面接不上。
第六题,他只能在“树退化成链”这种角落里捞一点残羹。
这些差距全都很具体。
具体到不像一句“再努力一点就好”能轻轻盖过去。
那天晚上,他吃饭吃得很慢。妈妈给他夹菜,他低头“嗯”了一声;爸爸想说点轻松的话,话到嘴边又咽回去了。整个家里都知道,这一回他不是考砸了,而是被更高年级、更高水平的选手真正结结实实地“虐”了一遍。
饭后,他一个人回到书房。
窗外已经全黑了,对面楼的灯一盏一盏亮着。书桌上还放着他前几天复习用的草稿纸,上面写着二分条件、线性筛、DP 状态,可现在看起来,都有点远。
他坐下,打开电脑,又把成绩页面看了一遍。
290。
三等奖。
只差 10 分。
看了几秒,他忽然把页面关掉,低头趴在了桌上。不是嚎啕大哭,也不是闹脾气,只是很安静地趴着,眼眶一点点发热。那种难过来得很慢,像秋夜的凉气一点一点爬上来。
他忽然很想起前两题那两块绿色,想起自己在考场里写出贪心题时心里那一下亮,想起后来被第四题彻底挡住、第五题只摸到第一层、第六题只能写条链的无力。
原来一场真正的大考,可以这样把人分得这么清楚。
你会什么,不会什么,差在哪,强在哪,题目全都替你摊开了。
妈妈轻轻推门进来时,他还趴在那里。
“伤心了?”妈妈问。
陈紫昂没抬头,闷闷地“嗯”了一声。
妈妈走过来,站在他旁边,没有马上说“没关系”。过了好一会儿,她才轻轻开口:“难过是应该的。”
这句话让陈紫昂鼻子更酸了。
因为它不是那种轻飘飘的安慰。它像是在承认:对,你确实被打疼了,你确实差一点没过去,你确实有理由难受。
“但你也看到了吧,”妈妈继续说,“你不是完全跟不上。你是能跟一段的,只是后面更深的地方,现在还没学到。”
陈紫昂慢慢抬起头,眼睛有点红。
妈妈拿纸巾给他,笑得很轻:“被五六年级的打疼一回,也不是坏事。至少以后你知道,你要补哪一块。”
这话很朴素。
可那一晚,陈紫昂坐在台灯下面,第一次真正把“被打疼”这件事,和“还能往前走”放到了一起。
疼是真的。
差距也是真的。
可前两题满分也是真的,第三、五、六题拼出来的部分分也是真的。那些分不是别人施舍来的,是他在一堆明显高过自己当前水平的题里,一点点捞出来的。
这说明他并不是站在门外看热闹。
他已经进场了。
只是场子比他想得更大,拳头比他想得更重。
窗外起了风,吹得树叶轻轻响。十月的夜已经有点凉了。陈紫昂坐在书桌前,吸了吸鼻子,把那张写着“290”的草稿纸慢慢折起来,夹进了笔记本里。
不是收藏荣光。
而是记住这次疼。
因为他隐约知道,这样的疼,往后大概还会有很多次。可也许正是这些被更高水平选手“虐”出来的时刻,会逼着他一点一点长出更硬的骨头。
那晚睡前,他没有再打开题库。
只是躺在床上,睁着眼看了很久天花板。脑子里一会儿是第三题那个怎么也优化不下去的转移,一会儿是第四题那堵他连门都没摸到的墙,一会儿又是二等奖线那个刺眼的 300。
可在这些乱糟糟的念头最后,留住他的,却是另一个更安静的画面——
机房里,前两题绿起来的时候,他心里那一下很轻、很亮的感觉。
他想,也许自己还会继续往前走。
哪怕下一次,还是会被打疼。
第七章:更高的山
CSP-X 的成绩出来以后,家里安静了两三天。
那几天,谁都没有再提“要不要放弃”这件事。
不是刻意不提,而是像一家人忽然默契地明白了什么。那张写着 290 分、三等奖 的成绩单,乍看并不漂亮,甚至还带着一点“差 10 分就到二等奖线”的遗憾;可真正把它放到四年级这个年龄层里去看,就完全不是那么回事了。
小周老师后来很直白地说过一句话:
“你这个分,在四年级里已经是前百分之几了。”
这句话像一颗很小的钉子,稳稳地钉进了整个家庭接下来几个月的空气里。
前百分之几。
意思是,明年五月那场四年级小学排名赛,陈紫昂大概率能拿到一等奖。再往好一点想,名次还可能不低,低到不只是“继续学下去没问题”,而是足够让一线、甚至顶级初中,真正把视线落到他身上。
于是“放弃”这个词,就这样悄无声息地退场了。
没有谁宣布,也没有谁总结陈词。只是饭桌上,大人们说话的方向慢慢变了。
以前是:“四年级要是不行,就别硬撑了。”
现在变成:“这几个月把树和图补上,明年五月应该会更稳一些。”
以前是:“再看看吧。”
现在变成:“要不要给他把寒假的综合训练再排紧一点?”
这种变化很轻,很家常,却比任何豪言壮语都更真实。
它说明,一家人已经默认:这条路,要继续往下走了。
十月到来时,济南真正凉了下来。
国庆后的第一场雨过后,树叶开始一片一片往下落。早上出门时,小区花坛边总铺着一层湿漉漉的黄叶,踩上去有很轻的碎响。风从单元门口灌进来,带着秋天特有的干净凉气。陈紫昂背着书包去上学,书包里除了学校作业,又重新塞进了越来越厚的算法讲义。
CSP-X 把他打疼了一次,也把接下来该补什么,打得很清楚。
树和图的问题。
二维动态规划、区间动态规划。
还有那些平时看起来不起眼、真到做题时却常常要命的 STL:map、set、bitset、priority_queue。
这些东西,一个个都不像“新玩具”,更像是一块块必须补上的木板。前面是河,你不补,明年五月过不去。
于是十月到来以后,陈紫昂的生活又恢复成了那种熟悉而紧凑的样子:上学、写作业、去机构、回家练题。窗外的树叶一天天变黄,风一天天变冷,他也在一天天往自己的薄弱处补。
最先补的是树。
小周老师讲树的时候,总喜欢先在白板上画一个圆圈,再从圆圈下方伸出两条线,像一棵刚刚长出枝杈的小树苗。
“树这种东西,看着复杂,其实很多题都得先学会顺着枝杈走。”
树的直径、树的重心。
这两个词刚摆出来的时候,陈紫昂觉得它们既陌生,又有一点奇怪的诗意。可真学起来,就一点也不诗意了。树的直径,说白了就是树上最远的两点之间那条路;树的重心,则是问你砍掉哪一个点以后,剩下的最大连通块最小。
小周老师先讲树的直径,说最经典的做法是“两遍 DFS/BFS”:先从任意一点跑到最远,再从那个最远点跑一次,最远的距离就是直径。
陈紫昂第一次听懂的时候,心里轻轻亮了一下。
原来树上最远路,不用傻乎乎两点两点去试。你可以先扔一颗石子,顺着最远的水纹找到边缘,再从边缘往另一头量。这种做法有一种很利落的聪明。
可到了树的重心,他就没那么轻松了。
因为这题不只是“会跑”就够,你还得在 DFS 的时候顺手算出每棵子树大小,再去比较“砍掉当前点以后,最大的那一块有多大”。这对一个四年级孩子来说,已经明显不只是“顺着树往下走”那么简单了。你得一边走,一边带着信息回来,像在树林里去了一趟,还得记住每棵树大概有多粗。
有一天晚上,他在草稿纸上画了一棵很丑的树。
一个圆圈连着两个圆圈,其中一个再往下分出三条,另一边又歪出去一根。草稿纸上的箭头乱七八糟,节点编号也写得有些斜。妈妈端着热牛奶进来,看了一眼,忍不住笑了:“这像蜘蛛网。”
陈紫昂抬头,很认真地说:“这是树。”
“那你现在在干吗?”
“找重心。”
妈妈点点头,把牛奶放下,没再多问。
她当然看不懂“重心”到底是什么。可她能看懂,儿子这次不是在背几句代码,而是真的在和一棵看不见的树较劲。
图的问题比树更冷一些。
树至少还像有枝杈的东西,图则更像一张复杂的路网,边和点交织在一起,一眼看过去就容易让人头皮发紧。
最小生成树、单源最短路、全源最短路。
这些名字从小周老师嘴里说出来的时候,教室里明显安静了几分。前排有几个高年级孩子神情还算平静,陈紫昂却下意识坐直了一点。
他知道,自己又走到一段更深的水里了。
最小生成树刚开始学时,他还觉得有点意思。Kruskal 的思路很形象:把边按权值从小到大排好,一条条试,能连就连,直到所有点都被连成一片。这种“先排序,再尽量不吃亏地选”的味道,和他之前学过的贪心有相通的地方。
可到了并查集那一步,他就又容易晕。
“这两个点是不是已经连在同一个集合里了?”
“父亲是谁?”
“路径压缩以后怎么又变了?”
代码不长,逻辑却弯。陈紫昂第一次自己手写并查集的时候,变量关系被他改乱了,连着连着,集合竟然被他弄得莫名其妙断开又接上。屏幕前一片红字,他盯着看了很久,额头都发热。
单源最短路则更让人难受。
Dijkstra 一讲,priority_queue 就跟着来了。小根堆、松弛操作、当前最短距离,小周老师在白板上画了一个点,向外伸出好几条边,一边讲一边说:“你可以理解成,从起点往外一圈圈推,优先把当前最近的点确定下来。”
“像什么?”有人问。
“像水往外漫。”小周老师说。
这个比喻很好,可真到写题时,水并不会自动替你漫过去。你得自己维护队列,自己判断当前弹出来的是不是过期状态,自己把更优的距离重新压进去。陈紫昂写第一道最短路题的时候,调了很久。样例倒是过了,可一到多组数据、图稍微稠一点,他就开始乱。
最让他头疼的是全源最短路。
Floyd 那种三重循环,for(k) for(i) for(j),看着好像朴素,背后却藏着一种非常冷静的推理:允许经过前 k 个点时,i 到 j 的最短路如何更新。小周老师讲到这儿,白板上的式子写得很整齐,陈紫昂却听得有点发怔。
因为这种推理,已经不像是在做题,更像是在学一种新的思考方式。
你不能只问“这条路怎么走”,还得问“如果我允许经过更多中转点,它会不会变得更短”。
四年级的秋天,就在这些一层比一层冷的图论题里,一点点往深处走了。
如果说树和图让他觉得世界突然变大了,那么二维 DP 和区间 DP,则让他第一次感觉“状态”这东西真的开始长出层次来了。
一维线性 DP 他暑假已经摸熟一些了,至少看到很多题时,脑子里会本能地浮出 f[i] 的影子。可二维 DP 一来,状态忽然从一条线变成了一张面。
行和列,两个维度一起变化。
小周老师讲网格路径类题的时候,陈紫昂还能勉强跟上:从左边转移来,或者从上边转移来,边界先处理。可一到更复杂的二维状态,比如“前 i 个物品、容量 j 时的最优解”,或者带着额外限制的双维度 DP,他就容易在纸上写成一片小方格,越看越像迷宫。
他常常要把状态表完整地画出来。
一格一格填,一格一格推。
别人可能觉得这样慢,可他不画,脑子里就没有图。没有图,状态就像浮着的。只有先把一张表实实在在画在纸上,看着数字从左上慢慢往右下长,他才觉得自己真抓住了这题的骨头。
区间 DP 更麻烦。
这种题不像从左往右推,而是从短区间往长区间合,像拼木板。你得先算长度为 1 的,长度为 2 的,再到长度为 3 的……小周老师讲石子合并那类题时,在白板上写了一排数字,又在它们上面画了很多括号。
“区间 DP 最怕什么?”他问。
没人答。
“最怕你脑子里没有‘先后顺序’。”小周老师敲了敲白板,“你得知道,长区间的答案,是拿短区间拼出来的。短的没好,长的就根本站不住。”
陈紫昂听得很认真。
可真做起来,还是卡。
尤其是枚举分界点的时候,他经常会漏一种切法,或者前缀和算区间代价时边界搞错。一道石子合并,他在晚上改了三次还没过,最后发现不是转移错,而是区间和 sum[r]-sum[l-1] 那里,l 恰好为 1 的情况忘了特别处理。
那天夜里已经挺晚了,窗外一点风也没有,整座城市像静静沉在冷空气里。书房里只有台灯的暖光和电脑屏幕的白光,一冷一暖,照在他脸上。发现错误的那一瞬间,他先愣了愣,随即气得用铅笔在草稿纸上重重画了一道横线。
可气过之后,他又觉得有点想笑。
原来很多难题,不是真有多高深。很多时候,是你终于走到那儿了,可在门口被一颗小石子绊了一下。
除了树、图和 DP,小周老师也开始系统给他补 STL。
map、set、bitset、priority_queue。
这些东西不像一个完整模块,更像很多题里随时会冒出来的小工具。平时不学,做题时就总会觉得“如果我会这个,好像会快很多”;真开始学,又会发现它们每一个都有自己的脾气。
map 像一本自动排好序的字典。
set 像一个不允许重复的盒子。
bitset 像一长排能一格格开关的小灯。
priority_queue 则像一个永远先把“最重要那个”推到你手边的篮子。
小周老师讲 priority_queue 时,特意停了一下,说:“以后做最短路、做一些维护最大最小值的问题,经常会用到它。别嫌它只是个工具,真上赛场,工具不熟,题就会死在你手里。”
陈紫昂记住了。
他学这些 STL 时,反而有一种很朴素的快乐。因为它们不像某些难题那样需要灵光一闪,而是更像给自己多准备几把顺手的螺丝刀。今天记住一个函数名,明天会多一个操作方式,后天再看一道题,忽然发现:啊,这里原来可以用 set 判重,或者用 map 统计,省掉一大堆麻烦。
这种“工具变顺手”的感觉,像冬天里一件衣服终于穿暖和了,没那么激烈,却让人踏实。
日子就这样一天天往前走。
十月的风,十一月的雾,十二月越来越短的白天,像一层层纸被翻过去。陈紫昂还是那样,在学校和机构、作业和题单之间来回跑。只是这一次,他明显比暑假那阵更稳了。
不是因为不累,而是因为越来越知道自己在补什么。
年前最后一次阶段统计时,小周老师把他的平台记录翻出来,看着那几行数字,点了点头。
入门 300 题。
普及- 400 题。
普及/提高- 250 题。
普及+/提高 50 题。
这几个数字静静摆着,像四层往上叠的小台阶。
如果只看最前面的孩子,这样的题量算不上吓人。可对陈紫昂来说,这已经是整整一个秋冬一点点磨出来的。是天黑得很早的傍晚,是台灯下画过无数棵树和状态表的草稿纸,是风吹着窗户轻响时他还坐在桌前改一段 Dijkstra 或区间 DP 的夜。
有时候他自己都觉得不可思议。
半年前,普及/提高- 还像一道冷门;现在,它已经成了题单里会反复出现、也反复被他拿来磨的地方。至于普及+/提高,那五十道题里当然有不少他是靠题解、靠补做、靠老师点拨才真正吃下来的,可那毕竟说明:他已经把手伸到那一层门口去了。
年前那几天,济南开始真正冷下来。早晨出门时,单元门外的水泥地白白一层霜,风从脸旁边刮过去,像小刀。可书房里的灯依旧亮着,电脑屏幕依旧冷白,洛谷主页一打开,还是熟悉的一排排题单和记录。
陈紫昂坐在椅子上,越来越能感觉到一种不太明显却很真实的变化。
他不再只是“会一些题型”的孩子了。
他开始有了一点自己的骨架。
最有意思的事,发生在一月。
那年冬天,山东省和江苏省组织了一次小学组联考。
消息刚传出来时,家长群里就热闹了一阵。因为大家都知道,江苏的竞赛实力一向更强。那边的孩子练得早、练得深、题也更难。和他们联考,等于是把山东这边的小学生一下子扔到更高一档的水里,看看会不会呛着。
这次联考只有机试。
没有初赛那种理论题和程序分析,纯粹三小时多一点,几道上机编程题,直接见真章。
小周老师说这话时,语气平平的:“江苏那边的题风会硬一点。你们别指望有多温柔。”
陈紫昂听着,心里反而有一种说不清的兴奋。
可能因为他已经被 CSP-X 打过一次,也知道“更高一档”大概是什么感觉了。再往上去见一见,也就没那么怕。
联考那天,天冷得厉害。
机房外的树枝全秃了,风吹过去,发出很轻的摩擦声。陈紫昂缩着脖子进考场,手刚从外面带进来,还带着一点凉意。机房里空调开着,可座位一坐下,屏幕一亮,他还是下意识吸了一口气。
题目发下来以后,他很快就意识到——这次确实比 CSP-X 更难一点。
不是那种“看起来就完全不会”的难,而是那种每一道题都更硬一些,更不肯给人留舒服空间的难。前面题没有那么平顺,后面题则更明显地要求你看出性质、敢做取舍、敢用熟练的套路压过去。
陈紫昂这次发挥得并不算好。
有一道题他明明前面判断都对,最后因为一个边界漏掉了整一段情况;还有一道题本来能多拿些分,可中间切换题目节奏时有点乱,回来再看,脑子里的线已经断了。整场比赛做下来,他一直觉得自己像在一条有点湿滑的坡上跑,脚下踩得不算稳。
最后分数出来,是 290 分。
这个分数不能说差,尤其放在省级联考里,已经算是很能打了。可陈紫昂自己知道,这回自己发挥比较一般。很多本来该拿稳的地方没完全拿稳,该捞的分也没有全捞到。
所以成绩出来以后,他的心情并没有太大起伏。
直到榜单放出来。
那是一个很冷的晚上。
妈妈把电脑搬到餐桌边,一家三口围着看榜单。网页打开的时候,房间里很安静,只有暖气片轻轻的声音。榜单一行一行往下排,名字、年级、分数、耗时,清清楚楚地列着。
陈紫昂先是找到了自己的名字。
290 分,不高不低,位置也还行。
然后他下意识往榜单最前面看去。
第一名,裴嘉明,四年级,满分。
第二名,仇牧达,四年级,满分。
再往后,才是一串五、六年级的名字。
陈紫昂怔住了。
不只是因为前二是满分。
更因为这两个满分,还是在一群更高年级的孩子头顶拿到的,而且耗时最短,压得很干净,像两块沉甸甸的石头稳稳压在榜首。
家长群和选手群立刻炸开了。
“这俩四年级是谁啊?”
“满分还最短耗时,太离谱了吧。”
“下面一堆五六年级,全被压着。”
“这是真被单调队列了。”
所谓“单调队列”,原本是算法里的东西。可群里有人拿它开玩笑,说:当一个选手比你小,还比你强,而且强得很稳定,那你就永远也打不过他了。 像单调队列一样,把后面的人一个个无情弹出去。
这句玩笑话一出来,群里立刻一片“哈哈哈哈”和“太真实了”。
陈紫昂却没有笑太久。
因为他盯着榜单前两行名字,心里忽然升起一种很安静的震动。
裴嘉明。
仇牧达。
这两个名字以前他没认真见过,可那天晚上,它们忽然像两颗很亮的钉子,钉在了冬天的夜里。
后来有人在群里发了联考结束后的合影。
照片上,一群孩子站在机房楼前,外套裹得鼓鼓的,脸都被风吹得有些红。站在前排中间的两个男生,一看就和别人不太一样。不是因为他们站得有多张扬,恰恰相反,他们看上去都挺朴实。
裴嘉明有点胖胖的,脸圆,脖子也显得结实,穿着一件深色羽绒服,站在那里像个小墩子,眼睛却亮,笑起来有点憨。
仇牧达也是壮壮的,个子不矮,脸颊饱满,手揣在口袋里,神情有种很自然的稳。
他们看起来都不像那种“天才”该有的、夸张锋利的样子。
反而很像冬天里最常见的那种结实孩子:跑步会呼呼带风,打球可能也不差,饭量说不定还挺好。可就是这样两个有点胖、有点壮的四年级男生,站在榜单最顶端,把后面一大串五六年级学生压得没有一点脾气。
那种反差,让人越看越觉得不可思议。
陈紫昂盯着照片看了很久。
他本来以为,真正的绝世天才应该像刀一样,瘦、冷、亮,一眼看过去就让人不敢靠近。可裴嘉明和仇牧达不是。他们站在人群里,甚至有点像学校里随处能看到的那种普通男生,壮壮的,脸上还有一点没退干净的孩子气。
可就是他们,拿了满分。
就是他们,把一整张榜单排成了那种让人看一眼就服气的样子。
那一刻,陈紫昂心里第一次非常具体地明白了:有些人真的不是“比你多学一点、多练一点”那么简单。
他们像是脑子里天生有一条更直、更亮的路。
普通人要绕很多弯、补很多洞、熬很多夜,才能勉强摸到的门,他们可能只是看一眼,就进去了。
对于这样的人,普通人只有仰望。
那晚睡前,陈紫昂躺在床上,脑子里还在反复浮现榜单前两行的名字。
裴嘉明。
仇牧达。
还有那句被大家笑着发出来的话:被单调队列了。
他本来也想跟着笑的,可笑到后来,心里却只剩下一种很安静的复杂感受。
一方面,他确实有点服气。
这种服气不是沮丧,而是“啊,原来真的有这种人”的那种服气。就像你一直在一座山上往上爬,突然抬头,看见更高的山尖上已经站了两个和你差不多大的孩子,风吹着他们的羽绒服,他们却像一点也不冷,甚至还有空回头看看下面。
另一方面,他也有一点不服。
不是不服他们,而是不服自己只能站在下面看。那种感觉很微妙,像你明知道眼前这座山不是一时半会能爬上的,可你还是会忍不住在心里想:那我能不能先往上再走一点?哪怕离他们还很远,哪怕只是少仰一点头也好。
窗外的风吹着窗框,发出轻轻的响声。冬夜很深,房间里只有一点微弱的路灯光从窗帘边漏进来。陈紫昂在被子里睁着眼,忽然觉得这个冬天好像忽然多了一种新的意义。
以前他补漏洞,是为了明年五月的四年级排名赛,是为了争一等奖,是为了让更好的初中看见自己。
可现在,在这些现实目标之外,他心里又多了一点别的东西。
一种很模糊,却又很真实的念头:
原来这条路上,真的会遇到这样的人。
那自己以后,会不会有一天,能走到他们身边去?
他当然不知道答案。
那时的他只是个刚满四年级不久的小孩,坐在山东冬天的寒夜里,刚刚在一张联考榜单上看见两个未来会和自己并肩的名字。此刻的他,只能隔着屏幕仰望他们,像仰望两颗很亮、很近,却又还够不着的星。
可星星之所以会被人记住,恰恰是因为它们先出现在很远的地方,若有人循着它,便不算迷路。
他已经开始朝那边走了。
第八章:五月这场仗
五月的济南,已经有了初夏的样子。
风不再冷了,吹过操场边的杨树时,会带起一层新叶细细的响。早晨七点多,太阳还不算太烈,光落在教学楼外墙上,像刚刚铺开的一层薄金。校门口卖煎饼和豆浆的小摊已经支起来了,白气往上冒,家长们站在路边说话,语气压得很低,可那种紧绷的期待,还是从每个人的眉眼里透出来。
陈紫昂背着书包,从车上下来时,个子比一年前明显高了些,肩也比以前更挺。他还是有一点瘦,可整个人已经不再是那个刚学数组就会为下标发愁的小孩了。过去这半年,他被题打过,被更高年级“虐”过,也一点一点把树、图、DP、STL 和各种乱七八糟的漏洞补了上来。那些东西没有把他一下子变成谁口中的“天才”,却把他慢慢磨成了一个像样的竞赛生。
今天是四年级小学排名赛。
三小时,六道题。
题目难度大致会落在:入门、普及-、普及/提高-、普及/提高-、普及+/提高、普及+/提高。
这是他真正等了一整年的比赛。
去年的三年级排名赛,他做出四题,二等奖。那时候他已经算不错,可还不够亮,不够让顶级初中认真停下来看他。可现在不一样了。现在的他,已经在更大的联考里拿过分,已经在更高水平的题面前被打出过伤口,也已经知道自己该怎么在三小时里分配体力、取舍、抢分。
走进考场的时候,他心跳还是很快。
但那种快,和一年前很不一样。一年前更像是茫然,是小孩子第一次被推进真正赛场时那种“我会不会一上来就摔倒”的慌;这一回,他更像是带着一身已经磨过的伤,站到场边,心里清楚今天会很难,但也清楚自己不是全无胜算。
妈妈在门口替他理了一下衣领,只说了一句:“把你会的写出来。”
陈紫昂点点头。
阳光从走廊尽头照进来,地砖亮得有些晃眼。他跟着人流走进机房,看见一排排电脑整齐地亮着,空调冷气缓缓吹出来,把机房里那种熟悉的、带着塑料和灰尘气息的味道压得很平。很多孩子都比他看起来沉静,有人把手按在鼠标上,有人闭着眼睛像在最后过一遍题型。监考老师宣读规则的时候,整个房间安静得几乎只能听见空调出风口轻微的风声。
屏幕亮起,题目发下来。
陈紫昂先从头到尾扫了一遍。
第一题果然比较基础,是一道模拟。
第二题是一道普及- 的枚举或排序类题。
第三题开始有点凉意,明显到了普及/提高-。
第四题也在这个层级,但风格不一样。
后面两题,一眼就知道不是舒服题。
慢慢沉下来。
很好。至少结构没有偏得太离谱。
这已经足够了。
第一题题面不长,讲的是一个小系统里几种状态的切换。输入一串操作,要求最后输出某几个量的结果。看着不吓人,但细节挺多:有一次清零操作,有一种状态只能在前一状态下触发,还有一个地方如果顺手写过去,很容易把“本轮操作”和“上一轮结果”混掉。
这种题,正是他现在最不怕的。
他没急着写,先在草稿纸上列了一个最小样例。操作一,状态变成什么;操作二,哪些量更新;操作三,如果碰到特殊情况,应该跳还是应该保留。几分钟后,他心里已经把整条流程顺过一遍,才开始敲代码。
变量开得很规矩。
每种操作一个分支。
每次更新之后都回头看一眼,有没有漏掉重置。
写到一半时,他还特意停下来,把自己最担心的那个边界又手推了一次——果然,如果按最开始脑子里那种顺手写法,会漏掉一次回滚。
他把那一块补上,跑样例,提交。
Accepted。
屏幕上跳出绿色的那一瞬间,他手指轻轻一松,整个人像先稳稳踩到了第一块石头。三小时的比赛里,第一题有没有顺利拿下,对心态影响太大了。尤其今年没有那种“看一眼就能写”的白送题,这道模拟能稳住,后面很多东西就还可以按自己的节奏来。
第二题看起来比第一题冷一点,但仍然在他擅长的范围里。
题目给了一串数,问在某种限制下,满足条件的方案有多少。乍一看有点像要暴力枚举,可数据范围又提醒你不能瞎套太多层循环。陈紫昂读到中段时,脑子里忽然亮了一下——这题其实不是让你硬枚举所有情况,而是让你先排序,再利用有序性去缩掉大量不可能的组合。
他立刻在草稿纸上写了一组数,按大小排开,然后试着固定前两个量,看第三个量能够落在哪一段范围内。写着写着,感觉就出来了:排序以后,很多比较关系会变得顺手,你甚至不需要真的把所有组合都跑一遍,只要在某些地方提前停下就行。
这种“枚举里带一点小优化”的题,他过去一年做过太多了。
不是华丽的算法,不是那种一眼惊艳的性质,可特别考验一个人有没有把基础真正磨熟。你得知道什么时候该继续往下枚举,什么时候已经可以断掉;得知道怎样在有序序列里利用单调性省时间;还得知道怎么写才不把边界条件搞丢。
他这一题写得比第一题还快一点。
中间有一处判断,他几乎是凭手感就写出来了——这种手感并不是天生的,而是几百道普及- 题一点一点攒出来的。交上去,第二道绿也出来得很顺。
两题在手,他心里已经有了一层很薄但很关键的底气。
他不是来试一试的。
他是来抢分的。
第三题开始,难度明显抬了一层。
题面讲的是一段路径上的若干位置,每个位置有代价或收益,问在某种约束下怎样取得最优解。表面上像个普通最优值问题,可细看就知道,单纯贪心不太稳,暴力更不可能,八成要往动态规划那边想。
陈紫昂看到这里,先没有慌。
因为这一年里,他最明显补上来的东西之一,就是 DP。
他先在纸上写下最朴素的问题:如果只看前 i 个位置,最好的答案是什么?
接着又往下想:这个答案是从哪里转移过来的?是前一个位置?前若干个位置?还是某种“选或不选”的关系?
他列了几个最小数据。
n=1 时答案是什么。
n=2 时如果两种情况都试过,会长什么样。
n=3 时转移是不是已经开始露头。
几分钟后,那条熟悉的线慢慢浮出来了。
确实是线性 DP。
状态不算特别花,但也绝不轻。你得先把 f[i] 定义得刚刚好,既不太宽导致转移不清,又不太窄导致信息不够。然后在每一个位置上,考虑是接前面的最优状态,还是在这里单独开一段新的选择。那种感觉,有点像在一排石头上过河:每一步都不能乱踩,但只要前面踩稳了,后面就会一块一块接出来。
他写的时候很专注。
机房里偶尔有谁拖动椅子,发出很轻的摩擦声;空调风吹在手背上,有点凉;可这些他都没太在意。他全部的注意力都落在那几个状态和转移上,像在一堆线里耐心地把最重要的那根抽出来。
写完后,他自己又跑了两组小数据,确认边界没塌。
提交。
评测转圈的时候,他胸口那一下还是紧了。毕竟这已经不是前两题那种相对稳的区间了。可几秒之后,绿色还是跳了出来。
第三题也过了。
那一刻,他盯着屏幕,心里突然有一种非常短暂却非常清晰的感觉:自己这一年,是真的长了。
如果是去年五月,这道题他大概连状态都不一定能找对;可现在,他已经能在这样的赛场上,把一题普及/提高- 的线性动态规划稳稳写出来。
这不是运气。
这是那些无数个午后、傍晚和夜里一点点推出来的。
第四题是这场比赛里最关键的一道。
不是难度最高,却最像一道真正的分水岭。能不能做出来,往往决定你到底是在“还不错”的那一档,还是能真正摸进前排。
题目读完以后,陈紫昂先是皱了皱眉。
它不再是纯 DP,也不是单一的贪心,更像是一道综合题:给定若干区间或点,问某个最小值或最大值在一串限制下怎么达成。表面上思路很多,实际上最靠谱的入口是——二分答案,再配一个贪心判定。
这是他被打疼过、也狠狠干练过的一类题。
尤其是在 CSP-X 和后面那几次模拟里,他早已经知道,这种题最可怕的不是代码,而是你得先看出“为什么能二分”。
他没急着写,而是先在草稿纸正中写了六个字:
答案有单调性吗?
然后他开始列样例。
如果答案取得很小,会怎样?
如果答案变大,条件是更容易满足,还是更难满足?
是否存在一个清清楚楚的分界线:某个值之前全可行,之后全不可行,或者反过来?
写着写着,他突然看见了。
是有单调性的。
而且判定也不需要多花,只要把原问题转成“给定一个答案 x,我能不能按某种策略从左到右扫过去”,用贪心地尽量早选、尽量少浪费的方法去验证,就够了。
那一下像黑屋子里突然亮了盏灯。
他立刻顺着这条线往下走。
写二分框架。
写判定函数。
在判定函数里从头扫,维护当前状态,遇到必须选的时候就选,尽量给后面留空间。
这题他写得并不轻松。
中间一度卡在一个边界上:如果当前正好等于临界值,应该算已经满足,还是必须再推进一步?这种地方一错,整题就会像骨牌一样全倒。他反复手推了两组数据,最后才敢落定。
提交。
这一次评测转圈的几秒钟,显得格外长。
他甚至听见自己心跳得有点重。
然后——绿。
第四题通过。
那一瞬间,他整个人不是轻松,而是先有一点发怔。因为他太清楚这意味着什么了。意味着他不只是把前面基础和中等题拿住了,连最关键的那道普及/提高- 也扛下来了。前四题全过,在四年级这场比赛里,已经足以让人真正站到前排去看风景。
可比赛还没结束。
后面两题,才是真正把名次往更深处拉开的地方。
第五题一打开,陈紫昂先是下意识地坐直了一点。
题面比前面几道都长,却不是那种纯靠读题耐心的长,而是一种一看就知道背后藏着“第二层”的长。
题目讲的是一排很长的观测点。
每个点上都有一个权值,你可以选择若干个连续段作为“保留段”,把这些段里的权值加起来;但题目又加了一条非常别扭的限制——不能出现长度超过
陈紫昂读完第一遍的时候,心里其实是松了一点的。
因为这题至少不像图,也不像树。
它还在“线”上。
在线上的题,总归还能想。
他先在草稿纸上写下最朴素的想法:
设 f[i][0/1] 表示考虑到前
如果第
如果第
因为你得往前看:这一段连续选中的尾巴到底从哪儿开始?
如果最后这一段是从 sum[i]-sum[j]。
写到这里,他心里轻轻亮了一下。
这题不是不会。
是 DP。
而且这个 DP 不是完全没有路的那种。
它甚至已经把路露出了一半。
他继续往下写:
这一步一落到纸上,他就盯住了。
因为他知道,真正的分水岭就在这儿。
如果只是写到这里,那就是一层枚举
小数据能过,大数据一定炸。
而题目既然敢把它放在第五题,就绝不可能只是让你写个朴素 DP 交上去。
他咬着笔帽,往下看那一行式子。
sum[i] 是当前固定的。
真正要在一堆 j 里找最大的,其实是
而且这个 j 还不是乱跑的,它始终只在一个长度为
看到这里时,他的心口已经开始发热了。
因为他隐约感觉到,正解就在纸后面。
只要再往前捅一层,就能从“朴素 DP”走到“维护一个滑动窗口最值”的那一步。
可偏偏那一步,他脑子里只有一个模模糊糊的影子——像是见过,像是老师讲过,像是某次题解里提到过,可你真要把它抓出来写成代码,它又像水一样从手指缝里漏掉。
他甚至想到了那个名字。
单调队列。
可想到名字,不等于会做。
机房里安静得厉害。
屏幕右上角的时间在一分一分往下跳。
有人敲键盘的节奏很快,像已经把这一题真正捋顺了;而他盯着草稿纸上那一行 max(f[j][0]-sum[j]),只觉得自己像站在门口,明明看见门缝里有光,却怎么也拧不开门把手。
这时候,过去一整年的实战经验开始起作用了。
他没有继续硬想“正解怎么维护”。
而是立刻低头去看子任务。
小数据。
果然有。
那一瞬间,他心里那根绷得很紧的线,反而一下子稳了。
够了。
先把这一层拿下。
他马上开始写朴素版本。
前缀和先预处理好。
f 数组初始化成很小。
f[0][0]=0。
然后从前往后枚举 i,每次把合法的 j 从 i-K 扫到 i-1,老老实实取最大值。
代码不花,也不优雅,甚至能看出一点“我知道这不是终点”的笨拙。
可它非常稳。
写到一半时,他还停下来,把“恰好连续选了
因为这种题最容易死在这里——思路明明没错,循环边界却多一格少一格,最后连部分分都掉得不明不白。
样例过掉以后,他没有再磨。
直接交。
评测结果出来,不是满分。
但也不是难看的个位数。
那是一串很明确的部分分,足够说明:小数据和中间那一层数据,他吃到了。
陈紫昂盯着那行分数,没有失落,反而很清醒。
这题今天自己确实还做不到正解。
可他至少不是站在原地发愣。
他是实打实地把题目拆开了,把朴素状态找到了,把前缀和接上了,再从子任务里硬抠出了一块完整的分。
对四年级的赛场来说,这已经不是“不会”了。
这是在和一道比自己更高一点的题,认真地过招。
第六题更像一堵墙。
题面一展开,就是一棵树。
根节点在最上面,下面分出一层层中转站,叶子才是真正的用户。
每条边都有传输代价,每个用户愿意付的钱又不一样。
你可以决定给哪些用户送信号、不送给哪些用户,但要求是:总盈利不能是负的。问最多能让多少用户收到信号。
陈紫昂读到第二段的时候,后背就已经慢慢绷紧了。
因为这题一看就不是“树的遍历”那种意思。
它也不是简单的“树上最远路”“树的重心”。
它更像是一整棵树上挂着很多小选择,而这些选择彼此依赖,要一起算。
他低头,在草稿纸上画了个很小的样例树。
根节点 1。
下面两个中转站。
每个中转站再挂两三个用户。
边上写成本,叶子上写用户愿意付的钱。
他盯着那棵很丑的小树,看了十几秒,忽然有一点感觉。
如果只看某个子树,问题其实可以变成:
在这棵子树里,选
这不就是 DP 吗?
再进一步,他很快写出状态:
设 dp[u][t] 表示以 u 为根的子树里,恰好让 t 个用户收到信号时,能获得的最大净收益。
叶子最简单。
如果这个点本身就是用户,那
- 选 0 个,收益是 0;
- 选 1 个,收益就是他付的钱。
真正难的是往上合并。
如果一个中转站下面有好几个儿子,那你得像背包一样,把每个儿子的贡献一层层并进来:
当前已经选了 i 个用户,下一棵子树里再选 j 个,合起来就是 i+j 个;
而一旦从父亲把信号送到儿子那边去,还得额外减掉那条边的传输费用。
写到这里,他几乎已经能闻到这题的正解味道了。
树上背包。
这三个字从脑子里冒出来的时候,他心里既亮又沉。
亮,是因为自己至少看懂了题的骨架。
沉,是因为他也很清楚:这种题最可怕的,从来不是“想到树上背包”这五个字,而是你接下来要真的把它一棵子树一棵子树地合,枚举容量、枚举分配、枚举子树大小。
这东西一旦写起来,转移就会像潮水一样往你脸上拍。
他还是开始写了。
先建树。
再 DFS。
每到一个点,先把自己的 dp 初始化。
如果是用户叶子,就给 dp[u][1] 一个支付值。
然后处理儿子,把儿子的 dp[v][*] 一层层和当前 dp[u][*] 合并。
刚开始那几行,他写得还算顺。
可一到真正的合并循环,整个人就明显慢下来。
for i 从当前子树大小往回枚举。
for j 枚举新儿子子树里取几个用户。
如果 j>0,还得把边权减掉一次。
如果某个状态原本就不可达,要跳过。
合并完以后,子树大小还要更新。
这已经不是“会不会某个知识点”的问题了。
而是在考你,能不能在三小时比赛的最后一题里,顶着体力和时间一起往下掉的压力,把一套厚得多的状态稳定地写完。
陈紫昂写到中段时,已经明显感觉手心在出汗。
他知道自己方向大概没错。
可也越来越能感觉到:这套朴素树上背包,多半只能吃一部分数据。
因为点数一大,子树一深,这种一层层暴力合并的代价会很重。
而且题面这种“有线电视网”式的结构,最可怕的不是普通树,而是那种几乎二叉、层层都分叉的数据。那种情况下,朴素合并会把时间磨得很可怕。他见到过洛谷题解里明确提到,常见的
也就是说,他现在手里的这版写法,是能得分,但不够。
这种感觉很难受。
不像第五题那样,你是清清楚楚卡在“单调队列不会”;
第六题更像是你已经走进了题里,已经知道自己在干什么了,却越走越发现这片林子比你想得更深,光凭现在这把斧子,砍不通。
可他还是没有停。
因为这时候比赛只剩下最后一些时间了。
停下也不会突然学会更高一层的树上优化。
能做的只有一件事——把手里这版朴素树形 DP 尽量写对,尽量让它把该拿的小数据和中间分都拿住。
他把 DFS 又顺了一遍。
把 dp 初始化查了一遍。
把“合并完儿子以后子树大小增加”的地方重看了一遍。
最后还专门加了一个判断:如果某个状态不可达,就别拿它去转移,免得把垃圾值带下去。
交上去的时候,他自己都知道,这不是满分代码。
但那一刻,他心里也很清楚:
这已经是自己在这道题上,能写出来的最像样的东西了。
评测出来后,第六题也确实不是零。
一截一截的部分分,像树枝上挂着的几片叶子,不算多,却不是空的。
他靠在椅背上,轻轻吐出一口气。
前四题全过。
第五题,靠看出朴素 DP 和前缀和,抠到一块不小的部分分。
第六题,靠树上背包的骨架和朴素合并,又抠到一块部分分。
这已经不是“运气好碰到的分”。
这是他这一年训练真正留下来的痕迹。
他知道什么地方该 full solve,什么地方该退而求其次;
知道正解想不到时,子任务就是命;
也知道在最后的大题前,不会的不是耻辱,空着才是。
机房里还是很安静。
有人还在最后几分钟里拼命点提交,有人已经靠在椅背上发呆。
屏幕右上角的时间一点一点逼近结束。
陈紫昂把前四题和后两道部分分代码又飞快扫了一遍。
第一题模拟,重看边界。
第二题排序枚举,确认循环终点。
第三题 DP,确认初值。
第四题二分,重点看判定函数里的等号。
第五题朴素 DP,重看 i-K 和 i-1 的范围。
第六题树上背包,重看 DFS 合并时的循环顺序。
他没有再幻想自己能在最后三分钟突然把第五题单调队列想出来,或者把第六题从朴素树形 DP 一下子提到更高一层。
他只是很安静地,护住自己已经抢到手的这些分。
结束铃响起的时候,他慢慢把手从键盘上拿开,才发现手指都有点僵了。
前四题,全过。
后两题,真刀真枪地咬下了两块部分分。
这一次,他不是“差不多发挥还行”。
而是真的在一场硬仗里,把自己能打出来的东西,几乎全打出来了。
走出考场的时候,走廊里很亮。
家长都站在外面,目光一下子就聚了过来。妈妈远远看见他,先从表情里判断了一遍,才走近问:“怎么样?”
陈紫昂背着书包,先抿了抿嘴,像是在脑子里重新数一遍,才说:“前四题应该都过了。后面两个……拿了点分。”
妈妈眼睛一下子亮了。
“前四题都过了?”
“嗯。”
她没有立刻大声说什么,只是很轻地吸了一口气,像怕惊动这点刚刚落下来的运气和实力。爸爸在旁边也明显松了一口气。
可真正的轻松,要等到放榜。
比赛后的那几天,陈紫昂表面上还算平稳,心里其实一直悬着。因为他知道,自己这份卷子大概率是不错的,但到底不错到什么程度,还得看全省其他人写成什么样。
尤其是后两题。
自己抠到的那些部分分,到底够不够厚?会不会别人已经完整杀穿第五题?前四题是不是全过的人其实很多?
这些问题都像细小的针,一阵一阵扎他一下。
可这一回,他没有像初赛那次那样完全没底。
因为他清楚地记得第四题绿起来时自己的感觉,也清楚地记得后面两题自己不是纯发愣,而是真把能抠的东西抠出来了。
这种“我知道自己写了什么”的感觉,会让等榜变得没那么虚。
放榜是在一个傍晚。
五月的天已经长了,傍晚六点多,窗外还亮着。书房的灯提前打开,电脑屏幕冷白,妈妈坐在前面,爸爸站在她身后,陈紫昂则站在一侧,手指不自觉捏着桌边。
网页刷开的时候,空气都像安静了一下。
成绩先跳出来。
一等奖。
再往下看名次——全省前 10%。
那一瞬间,书房里先是静了一秒,然后妈妈几乎是一下子转过头来,眼睛亮得像突然被点着了:“一等奖!前 10%!”
爸爸也笑了,难得笑得那么明显,声音都带了点压不住的热:“成了。”
陈紫昂自己反而是最后才真正反应过来的那一个。
他盯着屏幕,看着那几个字,看着那个位次,胸口先是发空,随后才一点一点被一种很热很满的东西填起来。
前 10%。
一等奖。
这不是去年那种“还不错,但还差点意思”的二等奖,也不是卡线进复赛、最后差 10 分到二等奖的三等奖。这是一个足够硬的、足够漂亮的、真正能让人往前看的成绩。
这说明他这一整年走的路没有白走。
说明去年五月那场比赛后,家里默契地不再谈放弃,是对的。
说明秋天补树、补图、补 DP、补 STL,那些冷风里和夜色里的题,没有白熬。
说明他在更大的联考里被五六年级打疼,也没有白疼。
这些东西全都压进了今天这张成绩单里,最后变成很简单的两个结果:一等奖,前 10%。
妈妈已经开始给老师发消息,声音里全是掩不住的喜气。爸爸在一旁说:“这回,一线学校肯定会认真看了。”
这句话一出来,房间里又安静了一下。
不是因为惊讶,而是因为一家人都知道,这句话是真的。
去年还只是“也许继续学学看”;而现在,这场四年级排名赛,终于把那道门真正往他面前推开了一大步。
陈紫昂坐下来,重新看了一眼成绩页面。
窗外树叶在晚风里轻轻晃,天边还有一点没退干净的晚霞。书房里是暖黄色的灯光和电脑屏幕的冷白光,两种光叠在一起,照得他的睫毛和笔记本边缘都很清楚。他忽然想起一年前,自己在三年级排名赛里也是前四题做完,后面拿了点分,可最后只是二等奖;那时候他走出考场,心里更多的是“还不够”。
可今天不一样。
今天他第一次真正觉得,自己把某个很重要的东西抢到了手里。
不是终点。
甚至远远不是。
可至少,在这一年最重要的这场仗里,他终于把自己从“还可以继续看看”的位置,硬生生往前推成了“会被认真关注的那个孩子”。
他静静坐了一会儿,心里没有想象中的狂喜,反而是一种很深的踏实。像你背着书包,在一条又长又挤的路上走了很久,终于在某个黄昏看见路牌,确认自己没有走错。
妈妈忙完了,回头看他:“怎么不说话?”
陈紫昂抬头,笑了一下。
那笑不大,却很真,眼睛里也亮着一点很轻的光。
“就是觉得……”他想了想,声音很轻,“这次终于打赢了一场。”
这句话一出口,连他自己都愣了愣。
因为他知道,这里的“打赢”,不是说从此无敌了,也不是说从此就能追上那些站在更高处的人。裴嘉明、仇牧达,那些名字依旧在更远的地方亮着;更难的题、更大的赛场、更强的对手,都还在后面。
可至少今天,至少在这场五月的四年级排名赛里,他是真的赢了一场。
赢过了过去那个只会羡慕别人的自己。
赢过了那个总觉得自己起步晚、怕追不上的自己。
也赢过了这一整年里,很多次差一点想要怀疑自己的时刻。
窗外的风更暖了一些,树叶沙沙作响,像有人在不远处轻轻鼓掌。
这一年的五月,终于给了他一个答案。
第九章:门开向更远处
五月的成绩出来以后,整个家里像是忽然安静了一阵。
不是那种压着气的安静,而是一种终于落地之后、连说话都显得轻了些的安静。一等奖,全省前 10%。这几个字摆在电脑屏幕上的那天晚上,妈妈一直到洗漱前都还时不时要回头看一眼,像怕它们会趁人不注意悄悄变掉。爸爸表面上还是稳,可第二天吃早饭时,也破天荒地主动提起了好几次“名次”“一等奖”“夏令营”这些词。
陈紫昂自己反而没有立刻陷进那种很热闹的高兴里。
因为他知道,这个成绩真正厉害的地方,不只是“一等奖”,也不只是“前 10%”,而是它像一把很具体的钥匙,终于替他把那扇一直远远悬着的门推开了一点。
一线初中会看见他了。
顶级初中,也会看见他了。
六月的最后一个周三,傍晚天色还亮着,妈妈的手机收到了一条信息。
那时陈紫昂正在书房里改一道图论题。屏幕上是最短路的模板,草稿纸上画着几个点和边,空调吹出来的风带着一点凉。他听见客厅里妈妈“啊”了一声,声音很轻,却有种压不住的颤。
“紫昂,出来一下。”
他拿着笔走出去,爸爸也从沙发边抬起头。妈妈把手机递过来,屏幕上那几行字不长,内容却让整个房间都像亮了一点。
是他所在城市那所顶级初中的通知。
因为四年级小学排名赛一等奖、同龄全省前 10%,学校愿意向他发放当年夏令营集训的名额。为期一个月,七月开营,封闭式训练,内容涵盖更系统的图论、动态规划、数据结构与综合训练。
手机屏幕很小,可那几行字落在他眼里,却像一下子把很多很多东西都照亮了。
这所学校,他当然早就知道。
不只是知道名字,而是知道它在家长群里的分量,在机构老师口中的分量,在一届又一届孩子身上的分量。那不是“还不错”的初中,也不是“有人能冲一冲”的初中,而是本市真正意义上最顶端的一所。它像一座高高的楼,很多家长站在楼下仰头看,很多孩子从很小时候起,就被轻轻推着往那个方向走。
而现在,这座楼,第一次主动朝他递来了一张入场券。
妈妈先笑了,笑得眼睛都亮起来。爸爸接过手机,又看了一遍,像是在确认自己没看错。陈紫昂站在一旁,笔还握在手里,指尖却有点发热。
“成了。”爸爸说。
妈妈回头看他,声音都轻了下来:“你拿到名额了。”
那一刻,陈紫昂心里并没有一下子炸开很大的欢喜,反而先涌上来一种很慢、很深的感觉。像你在一条路上背着书包走了很久,路边风景一直在变,脚下石头也一直在变,你以为自己只是往前走了一小段,可突然一抬头,发现原来已经走到了这扇门前。
他低头看了一眼自己手里的笔。
笔尖上还沾着刚刚写最短路时蹭到的一点黑色墨迹。
很多东西好像都还和以前一样,可又明明不一样了。
通知下来以后,小周老师很快也知道了。
那天下课比平时晚一点。七月前的风已经带着暑气,写字楼七层的走廊灯光微黄,教室里的空调开得很足,白板上还留着一道区间 DP 题的转移式,孩子们收书包时发出窸窸窣窣的声音。
小周老师把他留到了最后。
人都走得差不多了,窗外天色半明半暗,楼下车灯一串一串亮起来。教室一下子空下来,只有投影仪还发着浅蓝色的光。小周老师站在讲台边,手里捏着记号笔,看了他一会儿,才笑了一下。
“名额拿到了?”
“嗯。”陈紫昂点头。
“挺好。”
他说这两个字的时候,语气很平,和平常讲题时差不多。可陈紫昂还是听出来,那里面有一种很淡、很长的欣慰,像有人把一条绷了很久的线轻轻放松了一点。
小周老师把记号笔放下,转身在白板最边上写了几个字,又随手擦掉了,像只是给手找点事做。过了几秒,他才开口。
“夏令营去了以后,节奏会快很多。”
“嗯。”
“题会更硬,老师讲东西也不会像我这样慢慢带着你铺。”他笑笑,“有些内容,我其实已经快教不动你了。”
这话说得很轻,陈紫昂却一下子抬起头。
他从来没有想过小周老师会这样讲。
因为在他心里,小周老师一直都像一盏很稳定的灯。最初是小周老师把“洛谷”这个名字带进他的生活里,是小周老师告诉他什么叫模拟、什么叫枚举、为什么贪心不能瞎贪、为什么二分要先看单调性,也是小周老师一次又一次在他做错的时候,不急着替他把答案塞满,而是让他先去想“为什么”。
可现在,小周老师站在空了一半的教室里,很平静地说:有些内容,我已经快教不动你了。
这句话没有一点伤感的腔调,却一下子让陈紫昂心里酸了一下。
原来成长有时候就是这样。
不是谁大声宣布“你毕业了”,而是某个一直带着你往前走的人,忽然在某个傍晚很自然地说:再往前的那段路,你该去更高一点的地方走了。
小周老师像是看出他的情绪,笑着敲了敲桌角。
“别摆这个表情。教不动,不是坏事。”他说,“老师最怕的不是学生走太快,是学生一直走不出去。”
教室里安安静静的,空调出风口轻轻响着。
小周老师走到自己桌边,从一摞讲义底下抽出一本薄薄的本子,递给他。
“这个拿着。”
陈紫昂接过来,愣了一下。
那是一本很普通的横线本,封皮深灰色,边角有点磨旧了。翻开第一页,里面是小周老师自己的字。不是完整笔记,更像是一些零碎但很重要的东西:某类图论题最容易错的边界,二分判定里该先问自己的三个问题,树上 DP 合并时循环顺序为什么不能乱,还有一页只写了一行字——
会写,不等于会想;会想,不等于会取舍。
“我这些年自己带学生,顺手记的一些东西。”小周老师说,“不一定系统,但有时候比题解有用。以后去那边了,讲得快、练得狠,你脑子一热,很容易只顾着往前冲。这本子你留着,偶尔翻一翻。”
陈紫昂低头看着那本子,手指很轻地摸了摸封皮,半天没说出话。
窗外晚风吹过玻璃,楼下有人按了一声喇叭,声音隔得很远。教室里的投影光已经有点暗了,白板边缘的影子也慢慢往深里沉。那一刻,他忽然想起自己第一次来这里时,个子还矮一点,书包也小一点,坐在后排抬头看白板,连“洛谷”是什么都还只是一个模模糊糊的名字。
原来已经过去这么久了。
“小周老师。”他终于开口,声音有点低,“谢谢您。”
小周老师摆了摆手,像是不太习惯这种郑重其事的道谢。
“谢什么。”他说,“你题是自己做的,夜也是你自己熬的。我顶多算是最前面那一段路上,给你指了几个路牌。”
他说完,又停了一下,目光落到陈紫昂脸上。
“不过有一句话,你得记着。”
“嗯。”
“以后去更大的地方,见更厉害的老师、更多厉害的孩子,别因为自己现在能跟上一些,就以为以后什么都能靠硬冲。”小周老师顿了顿,“但也别因为会遇到更高的山,就觉得自己之前爬过的那些路不算数。你今天能拿到这个名额,就是因为前面这些路,一步都没白走。”
这句话落下来,像一块很稳的石头,压住了他心里某种说不清的晃。
陈紫昂点点头,很轻地“嗯”了一声。
那天走出写字楼的时候,天已经快黑了。楼外风还热,可天色深下来以后,空气里已经有一点夏夜的潮。妈妈在楼下等他,电动车停在树影旁边。他抱着那本深灰色的小本子坐上后座,回头看了一眼七层那扇亮着灯的窗。
玻璃后面,小周老师大概已经转回讲台边,继续收拾讲义,继续准备下一批孩子的题单。那扇窗并不大,灯也不算特别亮,可陈紫昂看着它,心里忽然有一种很难形容的感觉。
像是一个人站在桥这头,回头看见了桥那边一直替你提着灯的人。
桥并没有断,灯也没有灭。
只是从今晚开始,他要往更远的地方走了。
七月开营那天,太阳亮得有些晃眼。
顶级初中的校园比他之前来考试时还要显得大。校门口的石碑在夏天的光里发白,林荫道两侧的树长得很盛,叶子层层叠叠,把地上切出一块一块晃动的阴影。红色教学楼、宽阔操场、远处玻璃反光的综合楼,所有东西都显得比他原来的世界更大,也更稳。
很多孩子已经到了。
有人拖着箱子,有人背着装得鼓鼓的双肩包,还有家长站在树荫底下低声叮嘱。风从操场那边吹过来,热里带着一点草木味。陈紫昂站在校门内侧,忽然有种很不真实的感觉。
他以前总觉得,“顶级初中”是个说出来会带一点光的词。好像那不是一所具体的学校,而是很多家长嘴里高高挂着的目标,是一张张简历、一场场比赛背后隐约指向的地方。可现在,它真正立在他面前了。有校门,有树,有楼,有地上的热气,也有站在门口、背着书包、和他一样年纪不大的孩子们。
原来梦想落到地上以后,也是具体的。
报到、登记、分宿舍、领资料,一切都井井有条。老师们说话不多,动作却很快。发到手里的夏令营资料装在深蓝色文件袋里,封面印着学校的名字和夏令营期次。陈紫昂把它抱在怀里,走过教学楼门口时,下意识抬头看了一眼。
楼梯很宽,墙上贴着往届竞赛队员的照片和名单。那些名字他有些听过,有些没听过,但都带着一种安静的重量。照片上的人穿着校服,神情并不夸张,像只是很普通地站在那里。可正因为普通,才更让人觉得它们离现实很近。
也许再往后,自己也会在这里留下什么。
这个念头很轻,像蜻蜓点了一下水面,可一闪而过的时候,还是在他心里荡开了一圈很细的波纹。
宿舍里安顿好以后,下午就是第一课。
集训教室在综合楼四层。走廊开着空调,外面日头很烈,一进走廊反而觉得凉得有些发紧。教室很大,电脑整齐排开,前面的电子屏黑着,学生已经坐了大半。有人在低头看讲义,有人在和旁边的人小声说话,也有人只是安静地坐着,像在等某种真正重要的东西开始。
陈紫昂坐在靠中间的位置,书包放在脚边,资料袋整齐摆在桌上。他心里没有特别明显的紧张,更多的是一种很清楚的、身体微微绷起来的专注。
因为他知道,从这一节课开始,自己真的要进入另一段路了。
过了几分钟,门开了。
一个男人走了进来。
他不高,身形偏瘦,穿一件很简单的灰色短袖,眼镜很薄,神情平静得近乎冷。和很多孩子想象中“很厉害的教练”不太一样,他身上没有那种故意压人的锋利感,可教室却在他进门的那一刻一下子静了下来。
那种静不是因为大家都被吓住了,而是像所有人都本能地知道:这个人一开口,课堂就该真正开始了。
他把水杯放到讲台上,扫了一眼教室。
“我叫陈舟。”
声音不高,却很清楚。
“NOI 第 51 名,金牌。以后这一个月,很多课由我带。”
教室里没有人说话。
陈紫昂抬头看着他,只觉得这个名字像一块沉稳的铁,轻轻落在了教室中央。NOI 金牌,51 名,听起来只是一个简单的名次,可对坐在这里的孩子来说,那已经是很远很高的一层山了。更不用说,他不只是金牌选手,还是带过很多学生的金牌教练。那种威信不是靠声音大,也不是靠表情严,而是靠你站在那里,别人就知道你确实走过那条更高、更难的路。
陈舟没有废话,直接打开电子屏。
第一张投影上没有代码,也没有题目,只有一句话:
“从今天起,不再默认你们会做前四题。”
整个教室安静得更深了一点。
陈紫昂心里却轻轻一震。
这句话太准了。它像一下子把“夏令营”和“以前的机构课”之间那条看不见的线,清清楚楚地划了出来。过去一年,他很多时候的训练目标都是“前四题稳住,再去抠后面的分”;可陈舟第一句话就告诉他们:这里不是那样的地方。
“你们里面有的人,已经习惯把题分成‘会做的’和‘后面那两道’。”陈舟一边说,一边按下遥控器,“但在这里,后面那两道,迟早会变成你们必须正面啃下来的东西。”
屏幕切到第二页。
是一道题。
没有花哨的包装,题面冷得很,像一块直接摆上来的石头。图上的若干点与边,配一些限制,问最优值。陈紫昂看了十几秒,就已经知道这不是“第一天热身”的意思,而是陈舟故意要让他们从第一节课开始就明白:这里不打算让任何人舒服。
陈舟没有急着讲做法,而是先问了三个问题:
“第一,看完题以后,你脑子里最先冒出的错误做法是什么?”
“第二,这个错误做法为什么会错,是复杂度不对,还是思路本身不对?”
“第三,如果我要你在十分钟内只说方向、不写代码,你能不能把方向说清楚?”
没有人立刻答。
陈紫昂也没有。
因为他忽然意识到,陈舟在讲的根本不只是这一道题,而是在教他们一种更硬的思考方式:不是“我会不会这个知识点”,而是“我能不能迅速看出问题的本质在哪里、错法在哪里、路又该往哪里开”。
这和他之前熟悉的那种一题一题练、一个模块一个模块压的感觉很不一样。
更高,也更冷。
可也更像真正站到了门里。
十分钟后,陈舟开始在电子屏上写分析。笔画落下去很稳,像每一步都已经被他在脑子里走过很多遍。他不急着往代码上冲,而是先把题目拆得很薄——性质、约束、为什么不能暴力、为什么这个状态必须换一种想法去定义。讲到某个地方时,他只在屏幕上写了一行式子,没多解释,转头问:“谁能说出为什么这里要这样变?”
教室里沉默了几秒。
然后前排有人试探着开口。
陈舟没说“对”,也没说“错”,只是继续追问:“那如果这样,边界怎么办?”
这种追问让整间教室都显得更安静了。
陈紫昂坐在位置上,背挺得很直,眼睛几乎没离开过屏幕。他能听懂一大半,可也正因为能听懂,才更清楚另外那一小半有多硬。那些自己以前靠慢慢磨、慢慢补才摸到的东西,在这里被陈舟压成了一条更锋利、更直接的线。你要跟上,就不能只靠“做过类似的题”,你得真的看懂它是怎么转过去的。
那一堂课上到一半时,窗外太阳仍然很亮。热气在玻璃外面轻轻翻涌,楼下操场上有人跑步的身影一闪而过。可教室里冷气很足,电子屏的光打在每个人脸上,照得那些还带着孩子气的轮廓都显得格外认真。
陈紫昂忽然想起自己第一次在机构里听小周老师讲“洛谷”的那个晚上。
那时他坐在小小的教室里,抬头看着投影,只觉得“追一些回来”这句话像一颗很小的钉子,钉进了心里。后来他沿着那颗钉子,一路走过模拟、枚举、贪心、双指针、前缀和、二分、DP、图、树、一次次比赛和一次次被打疼,终于走到了今天这间更大的教室里。
原来成长并不是忽然哪一天就变成了“厉害的人”。
它更像是一层一层门被推开。
门后永远还有门。
而每推开一扇,你都会和过去那个懵懂的自己,轻轻拉开一点距离。
下课铃响的时候,教室里没人立刻起身。
陈舟合上电脑,只说:“今天晚上把这道题自己再做一遍。做不出来没关系,但明天我会点人讲第一步思路。”
说完,他拿起水杯就走了,步子不快,也不回头。
直到门在身后轻轻合上,教室里才慢慢重新有了声音。有人低声吸气,有人翻讲义,还有人苦笑着说“这第一课也太狠了吧”。陈紫昂坐在位置上,没有加入那些议论,只是低头把今天这道题的名字和几个关键点记进本子里。
他的字还不算特别好看,可一笔一画写得很认真。
写到最后,他忽然停了一下。
因为他发现,自己心里并没有害怕,反而生出一种很深的、几乎像渴望一样的东西。那种感觉像站在一条更高的坡前,明明知道上去会更累、更冷、更难,可你已经看见坡顶那一点很亮的光,于是身体里的某一部分会自然而然地往前倾。
窗外是盛夏的下午,树叶亮得发绿。
走廊里空调的冷气从门缝里轻轻漫进来。
桌上的深蓝色资料袋、小周老师送他的灰色本子、陈舟刚刚讲过的那道题,都静静摆在那里。
陈紫昂把笔轻轻放下,抬头看向前方空着的讲台。
那里已经没有人了。
可他知道,从这一刻开始,自己的下一段路,是真的开始了。
这一年,他终于告别了那个只会抬头看着别人、心里却还说不清自己想走多远的小孩。
而前面,更大的校园、更高的题、更锋利的老师、更辽阔的世界,正慢慢向他打开。
第一篇,到这里,也该收束了。
因为有些故事最好的结束方式,从来都不是“走到了终点”,而是——
一个孩子站在新的门口,眼里已经有了光。