THUWC 2020 (2019?) 游记

Day ?-?

THUWC 恰好在学考后一周,因此考完学考就进入了紧张的准备摸鱼中。停了两天课,还错过了学校里 IMO 二度金牌爷的讲座。

停课前打 USACO,在一道区间 dp 上卡了 3 h,最后交了一个假做法强行把那道题过了,剩下的就没有时间写了;后来发现 T2 是一道还比较可做的树上问题,没写有点亏了。停课的时候打了 Codeforces 的一场 Virtual Contest,感受了很神奇的暴力;又做了 COCI 的 Contest 3,感觉比去年的 COCI Open 难多了,竟出现了代码极长的淀粉质,直接放弃实现了。

感觉写的题目还是不够多,似乎本来可以写更多的题的。

Day 1-1

为了机票便宜先到 TJ 中转。零下的温度感觉确实比较冷,但是也没有预想中那么冷?需要穿的衣服居然没有超过 33 件。飞机上凭印象写了 NTT,居然默写正确了。

总感觉休息不足,非常困的样子。

Day 11

Day 1 要从 TJ 出发,体验了一下复兴号的城际列车,确实比 D 字头的车快了不少。接着乘地铁去报到,发了一个有花纹的布制品,本以为是格子衫,仔细一看,“精品围巾”。嗯,说实话出于某种原因我还是比较想要红色的围巾(bushi。

报到完就试机,这回是在系里的实验室考,电脑的系统似乎是 XUbuntu,不支持 Ctrl-Alt-T 打开终端似乎有些不习惯。看了看电脑里奇怪的软件,似乎只有 GIMP, Golden Dict 这两个,考虑到去年刚考图像,难道这回要考嘤语?桌面上还有 en.cppreference,难道要考毒瘤语言特性?反正根据这些信息,暂时猜不到 Day 3 考什么。

注:题意简述可以在 ouuan 的博客 找到。

试机

试机 T0 是 A+B,打了快读把它过了。

T1 是一个有点奇怪的题目,大概是说有 nn 个 A 类物品,权值分别为 a1,a2,,an(aiZ)a_1,a_2,\cdots,a_n(a_i\in \mathbb{Z});有 mm 个 B 类物品,权值分别为 b1,b2,,bm(bjZ)b_1,b_2,\cdots,b_m(b_j\in \mathbb{Z})。现在请你把这些物品从左到右排列成一排,计算总权值如下:对于每一个物品,如果它左边的物品与它同类,那么它对总权值的贡献即为 aia_ibjb_j;否则它对总权值没有贡献。特别地,最左边的物品对总权值没有贡献。要求总权值的最大值。似乎是一个奇怪的贪心,花了一些时间想,最后玄学地通过了。

T2 好像是一道奇怪的树上计数题,模数是 998244353998244353,而且旁边的选手还嚷嚷着让他的同学帮他调 NTT……看来这题不可做,直接跳了吧。

T3 是一道字符串,给一个字符串 AAqq 个字符串 BiB_i,要把 BB 插入到 AA 的某一个地方(比如 A=abc,B=deA=abc,B=de,那么可以得到 deabc,adebc,abdec,abcdedeabc,adebc,abdec,abcde 这些),求插入到哪里时得到的字符串字典序最小。照例是不会做,所以直接用 string 打了暴力,居然拿了 4040 分?

虽然试机有好多不会,但是反正也不太在意吧,所以也没有影响心态。

中午急忙吃完饭,就回去合影和开幕式了。合影意外地没有拖太久,开幕式也进行得极快,“讲 88 点”值得好评。

于是就到考场就位,略作等待之后,Day 1 就正式开始了。

Day 1 T1

一道很有意思的题目。

首先写了暴力,交上去拿点分爽一爽。因为觉得题目情景不复杂,所以便思考正解。我思考的点是“预处理当前工资计划是 ii 时,最终的工资计划应该是什么”,由于 kk 很小,因此对每一个员工弄了一个单调队列,在单调队列上二分。后来发现这样确实可以通过,而且预处理时怎么做,查询时也相应地怎么做。于是花了约 1h1 \mathrm{h} 愉快地通过了。

Day 1 T2

一道比较恶心的题目。

首先把比较容易的分数写了,接下来的却没有可以快速实现的思路了,于是决定去写 T3。

Day 1 T3

首先把送的 88 分拿到了,接着又陷入了长时间而无效的思考中……

据说北方冬天开了暖气的室内比较催眠,这是真的。再加上昨天晚上不适应这里的环境没有睡好,所以真的是越想越想睡。

强行去洗了洗脸,好不容易清醒了。回来看看还剩下的许多时间,决定回去写 T2。

T2 有一个“树”的 1313 分,似乎可以用树剖暴力地解决掉。再看一眼时间,写吧。

于是开始写树剖、线段树。时间过得很快也很慢,我不停地写啊写啊,简直都不知道自己在写什么。dfs1, dfs2, build_tree, pushdown, … 终于似乎是写完了,我甚至都不想再看代码,直接跑样例。修改了一点小的错误后,过了样例。

直接交!猛按几下 F5 过后,那个测试点旁边显示出了 Accepted 的文字。什么?我默写的树剖就这么通过了?我不放心地再浏览了一遍代码,找不出明显的问题,才终于判定——树剖应该是写完了。这是我 OI 生涯中至今考场上写的最长的代码了吧,总长度近 400400 行,大小近 10KB10 \mathrm{KB},可惜不能带回去纪念。

还剩大约半个小时,剩下的点似乎怎么也过不了,T3 也毫无思路。最后在玄学修改程序中度过消磨了最后半个小时。

Day 1 总结

Day 1 的话题目确实超出了我的水平范围吧,不过好在我在 2.5h2.5\mathrm{h} 时放弃 T3,并决定去写树剖,否则后半段真的要一无所获了。

Day 2

早上早起到西郊宾馆吃了早餐,种类还算比较丰富,大吃一顿后上了大巴。大巴上照例听了 Euphoria。

同样稍作准备后就开考了。

Day 2 T1

一道比较神奇的题目。

n(1n15)n(1\le n\le 15) 个函数 f1,f2,,fnf_1,f_2,\cdots,f_n,每个函数形如 fi(x)=aix+bix+cif_i(x)=a_i\vert x \vert+b_ix+c_i,给定初始值 xx,你需要安排函数复合的顺序,使得最终函数值最大;也就是你需要给出 1n1\cdots n 的一个排列 p1,p2,,pnp_1,p_2,\cdots,p_n,使得 fpn(fpn1(fpn2((fp1(x)))))f_{p_n}(f_{p_{n-1}}(f_{p_{n-2}}(\cdots(f_{p_1}(x))))) 最大。x,ai,bi,ci[15,15]x,a_i,b_i,c_i\in [-15,15],允许使用 __int128

首先还是写了 n10n\le 10 的枚举暴力。接着注意到 nn 很小,回想到我在 THUSC 2019 吃的亏,考虑状压 dp。状压 dp 要满足子问题最优特性,为了满足这一点,我在草稿纸上画了一下函数的图象,发现 fif_i 一定是拐点在 yy 轴上的一条折线,在 yy 轴两侧都是单调的。因此,使得 f(x)f(x) 最大的 xx 只有四种可能:最大的正数、最小的负数、最大的负数、最小的正数,也就是 ±\pm \infty 的极限和 00 的左右极限。

因此就有了一个很诡异的方法:把状态记为已经复合的函数的集合,也即 p1,p2,,pkp_1,p_2,\cdots,p_k 的集合;对每一个状态维护可能达到的“最大的正数、最小的负数、最大的负数、最小的正数”这四个值,转移的时候把前驱状态的所有结果塞到函数里,排个序,再把最小值、最大值、00 的前驱和后继全部塞到新状态的 vector 里面就好了。

没想到这种看上去十分暴力的解法居然过了,不过其实它的复杂度似乎是正规的 O(2nnlogn)O(2^n\cdot n\log n)

Day 2 T2

还是照例先写暴力,接着总觉得“部分分好像都可做”,然后去打部分分。首先是把 a=1a=1 的 Subtask 4 写了,然后发现自己欠考虑了,连稍大的样例都没过。接着就强行打补丁,把漏考虑的情况写了写,终于过了这个 Subtask。其他的 Subtask 好像也有些可做,我甚至还写了 Subtask 3 的程序,可惜是个假做法。想要打补丁,可越想情况越复杂,但写了代码又舍不得放弃,况且 T3 的劝退模数又让我不禁接着想 T2……

换题的时间到了,我最终还是放弃了其他 Subtask,把过了 Subtask 1,4 的代码重新交了上去。

Day 2 T3

写了暴力,结果暴力超时了,只拿到送的 11 分……

实在是太自闭了,在本地一看,n=10n=10 的预处理就要跑好久好久……根本过不了啊。

看来纯暴力是不行了。我再想了想,可以不用处理 A101+A102++A1010A_{10}^1+A_{10}^2+\cdots+A_{10}^{10} 种排列,只需要处理 1!+2!++10!1!+2!+\cdots+10! 种,最终把诸如 3,9,13,9,1 这样的排列映射(离散化)成 2,3,12,3,1 就好了。加了这个小优化,尽管本地跑得还是很慢,但是交上去居然过了?

旁边的同学其实给我一定压力,他不停地(真的是不停)大声(真的很大声)击打(真的是击打)键盘,嗯……

在想到“排列映射”的时候,我有了一个偷懒的想法:会不会“满足某种特征”的排列,它们的答案是一样的呢?如果找到这种“特征”,那我就不用写映射了。事实上,只要找到了“特征”,再加以打表,就可以告别暴力了!

于是我走上了漫长的打表之路。首先根据冒泡排序的特征,第 kk 轮后,第 k,k+1,,nk,k+1,\cdots,n 大值一定是已经归位的;因此如果没有归位,答案一定是 00

接着,我观察到,顺序排列 1,2,,n1,2,\cdots,n 的值很有特点;而其他排列就是在部分重复顺序排列的答案!只是重复的不太一样,有的重复的是 n1n-1 的,有的重复的是 n2n-2 的。

是什么决定了它重复的是哪里的呢?我首先试了逆序数,因为题面里提示了“逆序数不断减少”,但是并不对;我又尝试了“n1n-1 的位置”、“11 的位置”等奇奇怪怪的指标,发现都不对。

还剩一个小时,真是自闭啊。更神奇的是,旁边的同学还小声喊了一句,“我找到了”(之类的话)……

最终鬼使神差,我找到了规律——把这个排列做一轮冒泡排序(只做一轮!),这一轮中交换了 kk 次,那重复的就是 nkn-k 的顺序排列的前几个答案。真是……神奇啊。

然后就是写咯,知道规律了就能多拿好些分,但是更复杂的 Subtask 由于我不知道如何维护信息所以也写不了,最后还有约二十分钟的时候算是结束了所有可得分代码的书写。

Day 2 总结

Day 2 的启示主要就是要放弃吧,要是我不停地肝 T2 那恐怕是什么结果也没有。然后就是要坚持打表,即使一时找不出规律也要硬着头皮找(大雾)。

Day 3 (Day 2+)

Day 2 考完回去好好睡一觉,其实没有睡多长时间,就到 THU 的食堂体验了一下。THU 的食堂真的非常超值,44 元的排骨简直有 nnsz 两倍的量,这也许就是我们在那里买饭卡只能使用 70% 金额(充 20 可用 14)的原因吧,专项补贴挺多的。

由于 THU 的食堂太超值了,所以我吃不完,在还有不到半小时开考的时候骑车赶去考场。

到了考场发现笔不见了,我只有一支笔啊啊啊,这还是学习题,要做笔记的啊啊啊……

最后强行镇定下来,决定用 gedit 做笔记。

进场拿密码条,看到了“简单 Cache 系统实现”的标题,然后就在脑内回想,“关于 Cache 你有什么印象”,答案是什么印象都没有(雾),只知道这个东西和卡常数有关。

然后开考了。开考发现学习手册不知道在哪里,以为是我没找到,结果大家都没有。监考说“大家先看题吧”,结果这题没有学习手册根本看不懂,什么“Cache 一致性协议”,“MESI”?但是我“因祸得福”地看到了 T2 的一句话“这些算法的实现难度不一,请选手自行选择”,就明白了一定要做好放弃的准备。

终于发学习手册了,并且考试结束时间延迟 5min5\mathrm{min}。后来发现这 5min5\mathrm{min} 可能让我多拿了几十分。

先花了约半个小时看了学习手册,并且认真地用 gedit 做了提纲(没有 Typora,不能写 Markdown,不能看着左边的“大纲”,真不爽)。看学习手册的时候有意识地跳过了“伪最近最少使用”和某名字特别复杂需要参考论文的算法。可是看到最后的“Cache 一致性协议”就非常难受,不停地有“看不懂”的感觉,不过还是硬着头皮扫了扫,发现后来都是技术性内容,所以就跳过了。

看第一题,就是 Cache 一致性协议。而且就是要实现那些技术性内容。

好吧好吧,我写我写。把状态编了数字编码,然后照着状态转移的表格写了下去,由于以为后面的题目还要用到这个程序,我稍微注意了一下接口的实现(虽然还是一个很丑陋的过程式接口,没有 OOP)。写完照着样例调了调,发现出入还挺大,最终发现是我把不需要打印的 Flush 事件也打了出来。调整之后,就通过了。

第一题就把学习手册最后的内容给写了,那后来岂不是要很难?

结果看了第二题,发现考的是前面的“Cache 替换算法”。呃,这个不是很简单的数据结构的应用么——除了被我放弃的“伪最近最少使用”部分(这个比较难写的部分拥有其他部分两倍的分数)。再看一眼范围,n16n\le 16,本来都把堆的头文件引入了,干脆暴力了吧。暴力写完调一调,发现过不了样例?最终发现是自己被 T1 惯坏了,没有理解好“块”的概念,于是又重读 Cache 映射方式一节。经过几次尝试终于把“块”处理好了,这段处理“块”的代码也在之后的几道题一直沿用。

接着看 T3,只读 Cache 实现?充分利用 T2 的代码,似乎不难写好。原来 T1 那些复杂的东西在后面的题目都没有用啊。

还有一点时间,写一写 T4——读写 Cache 实现?似乎也不难吧,不就是要处理一下“脏”数据(没有与主存同步的数据)嘛。认真思考了一下,一个数据只有在写 Cache 的时候才有可能被标记为脏,只有在从 Cache 写入内存的时候才能消除脏标记,因此在这些时候对应维护好脏标记就行了。

写完发现不对,检查了输入数据的部分,发现脏数据部分要先读后写。改成先读后写之后还是错了,还剩几分钟,确实有些紧张,想着“也许这个调不出来了”。后来重新审读了那一段代码,发现问题是,改成“先读”之后读的数据把原来的脏数据覆盖了,“再写”的时候写的就不是那份脏数据了。于是把脏数据用临时变量存一下,就解决了。

交上去发现“00”和“11”的部分过了,“0,10,1”混合的部分却没过。仔细想了想,大概是因为“伪最近最少使用”出现了的缘故吧。

T5 是“某算法”实现?看上去完全不可做,但是它的题目和 T4 基本相同?想着出题人可能会放一点水,我把 T4 的程序交了上去,结果过了好几个 Pretest,也许白得了几十分啊。

最后还剩不到一分钟,就把我写的大纲交到 T6 “期待你的声音”上面去了。

比赛结束后看了看“伪最近最少使用”,其实也不是特别难写,但是要是我真的写了这种算法怕是要少几十分。又看了一眼“某算法”的论文,十几页英文论文。。比学习手册还长不少。我知道 Golden Dict 是什么用的了。

虽说我那几天停课也曾看过几眼嘤文论文,是一篇“O(n)O(n) 点分治”的(把树分成 O(nlogn)O(\frac{n}{\log n}) 块之类的,一看就觉得常数很大);但是这么长的,实在是……

Day 3 总结

感觉这场发挥非常好,应该是我目前学习题的极限了吧。

做大纲这一点应该挺好的,这样我对整个学习手册的内容就有一个把握;另外“想好什么东西在什么时候会改变”应该也挺重要。还有就是调试要有一定的信心,不要总想着自己会写错很多地方。此外要学会放弃!

停课那几天写了 CyaRon 语言(虽然是用 Python + regex 水过去的)、时间复杂度、LOGO 语言这些题目,也算是增加了学习题的信心吧。今后还可以把宝牌一大堆给补了。

面试与宣讲

考完机试还挺有信心,觉得这回应该能进面试了吧。

结果从 7:50 等到 8:10,一直没有电话。8:10 我去吃饭,在西郊宾馆餐厅等到 8:40,还是没有电话……

心里想着“我哪里写炸了呢”,然后又考虑早上去哪里玩。

结果 9 点左右突然接到我妈电话,说我爸刚刚接到电话。不是说好的 7:50~8:40 通知面试吗?然后我问“什么时候要去?”——“现在。”还好我早餐吃得差不多了——要是我决定去北京三环内玩,那我岂不是已经赶不回来了?

整理了一下仪容仪表——在此特别感谢我爸出发前帮我剪的头发——就去会议室报到了。之前有看 ouuan 的博客了解到大概的注意事项,所以就在等候的时候写自我介绍。在手机上写了 900 多字,又请我妈提意见,删到了 700 多字,于是就被叫到门外等候了。

等候的时候把稿子读了两遍多,就进场了。

没想到的是自我介绍只有 12min1\sim 2\mathrm{ min},我只好在说的时候自动删除内容。自我介绍的时候我还有一些紧张,不过强行调整过来了。

自我介绍完毕是面试官就自我介绍中的内容进行提问,居然问了“文化课成绩如何”、“你们学校多少人”、“每年多少个清华北大”之类的问题,之后就问了“你的嘤语怎么样”,自然过渡到嘤语阅读了?而且还要求出声朗读,感觉自己哑巴嘤语被发现了。文章里面大量出现 LBS(Location Based Services),还有 privacy,根据嘤语老师教授的嘤语阅读技巧——画框架,我算是大概理解了文章的 structure。之后面试官问我“What’s the passage mainly about”(事实上,是用中文问的),我就抓住了这两个词。似乎面试官还比较满意?

此后就问我开发的小程序的主要架构啊,还有我无意提到的 FFT 的原理啊什么的,感觉我回答得自己还算满意。接着计时器“嘀嘀嘀嘀”地响了,就顺势结束了。

感觉表现得还算满意吧,估计也是了解了大概流程的原因吧。

下午的宣讲居然没有讲题,差评。一开始的关于 THU 科研生活的宣讲还比较有收获,再次说明了我和强省学生在综合能力上的差距。后面的宣讲就显得主题不突出,十分催眠了。

据说今年发的纸非常多,那想必也是没有什么用的。

休闲娱乐和后续

晚上去了一趟国图,TP 的书真的非常多(比数理化加起来的两倍还多),可能是因为这个阅览室是面向大众的吧。然后各种大学教材特别多,不禁让人怀疑是否应该留更多的空间给专著,毕竟各种教材的内容都基本相近嘛。

第二天没做什么,下午就回校了。早上还有幸看到了一次小雪,不过雪粒就像碎屑一样小,也没有特别壮观……

一上文化课才知道,化学已经上到了“不认真学就会导致选修 4 完蛋”的化学平衡了,看来选 4 要完蛋了~(逃

致谢

这次首先要感谢父母的陪伴,让我在 BJ 都方便了很多。

接着要感谢老师同学们的鼓励,让我更有信心。

最后还是给 Hanakiko~

希望这篇流水帐一样的游记也能给今后去 THUWC/THUSC 的同学一点帮助吧。

评论