這真的是我打過最驚險,最一波三折的一場比賽,尤其是決賽的最后十分鐘,簡直是我打過比賽中最緊張刺激的最后時刻。。
由于這次比賽中,組委會要求我們?nèi)啼浧?,所以我才能在比賽過去一周后復(fù)盤,寫下這篇心得。。
先說下去年的情況吧。
去年我初賽排名大概80+,沒有入圍決賽。但學(xué)校有一個推薦名額,我就去西安參加了決賽。
決賽上午一道題也沒有寫出來,并且坐在我旁邊的劉星宇同學(xué)上午直接AKPPC(做出了所有的編程題),沖到了Rank1。
好在下午做出來了兩道PPC,之后從Rank16-掉到Rank19。。(記住這個19名,后面要考)
而第16名正好也是1000分,只是比我做得快了一點(diǎn),也就是說,我只需要再做一道題,就可以把他擠下去,拿到三等獎(前16名就有獎)。
再說說今年和去年的不同吧。
今年的決賽在線上舉行,使用雙機(jī)位+錄屏來監(jiān)考,全程不允許連接外網(wǎng)。而上次比賽直接在體育場里用局域網(wǎng),外面還放了信號屏蔽器,兩位選手就有一位志愿者監(jiān)考。去年的比賽是可以申請去用組委會提供的連接外網(wǎng)的機(jī)器的,只是時間有限制(貌似是30min)。
這次的比賽分為上下兩個半場,從9點(diǎn)到17點(diǎn),中午2個小時的休息時間,把上下午兩個階段分開,為了避免大家作弊,下午有新的題,不能繼續(xù)做上午的題。
去年的比賽真的挺遺憾的,可能是我當(dāng)時還不知道我離三等獎那么進(jìn),也可能是我看到所有的題目我都不會,我在比賽結(jié)束前1小時就基本放棄了,也沒有過多關(guān)注排名,最后只拿了個「優(yōu)秀獎」,和三等獎和5k元獎金“交臂失之”。
初賽實(shí)在9月13日舉行的,共有200多名選手參加,據(jù)說還有11歲的選手。從上午9點(diǎn)到晚上6點(diǎn)一共9個小時。
上午,開賽一分鐘后我拿到了 PPC-量子波動速讀 一題的一血(第一個做出來的),然后非常出乎意料地拿到了短暫的榜一。之后我有寫了兩道PPC題,但都沒有通過。
下午去學(xué)校寫題,用binwalk把簽到題解決了,然后一直在搞PPC。
在離比賽結(jié)束還有1小時時,我看到我的排名是30+,可能進(jìn)不了決賽,之后我又用C++重新寫了一遍那道PPC(之前是用Python寫的)。最終在5:40,也就是離比賽結(jié)束還有十幾分鐘的時候A掉了這道題,成功擠進(jìn)了前30。
我本來以為這已經(jīng)夠刺激的了,沒想到后面還有更刺激的。。。
決賽前一天,也就是周五(9月18日),我早早回到了家參加設(shè)備調(diào)試。調(diào)試過程中,我的設(shè)備非常完美地運(yùn)行著。調(diào)試結(jié)束后,我非常手殘地更新了一下系統(tǒng),然后就出事了。。
系統(tǒng)中的桌面和頂部菜單欄都出了問題,啟動失敗。我的電腦自今年2月份重裝系統(tǒng)以來,一直沒有出過啥大問題,然而到了這關(guān)鍵時刻出鍋。。還好除了某些操作稍微有些麻煩,所有其他軟件和功能都沒有受到影響,算是不幸中的萬幸了吧。雖然不會影響到我第二天的比賽,但對我當(dāng)時的心態(tài)是個很大的打擊。
順便說下,后面錄屏上沒有時間,就是因?yàn)檫@個組件壞了。
8點(diǎn)開始,選手們就陸續(xù)進(jìn)入會議室,到比賽開始時,我們組里所有選手的設(shè)備都已經(jīng)調(diào)試完畢。
比賽開始后,我發(fā)現(xiàn)平臺上只放了一道PPC(也就是傳統(tǒng)編程,是我最擅長的領(lǐng)域)題,這可怎么打。。
我和其他選手陸陸續(xù)續(xù)地A掉了這道題,之后我的排名為14,還是做得太晚了呀。之后,我并沒有在上午解出新的題目,排名一路從最初的14掉到了19。
下午的比賽非常有意思,我把它分成了幾個不同的階段,每個小標(biāo)題的時間是比賽開始后經(jīng)過的時間,也就是我錄屏的持續(xù)時間,如40:00指的是比賽開始后40分鐘。由于我們要求提前開錄屏,錄屏的時間會比比賽持續(xù)時間長2分鐘,這對最后爭分奪秒的時刻非常重要,我也懶得剪了,各位自己計算真實(shí)時間吧。
下午開始時,放了兩道PPC題,按照我的策略,先把題都看一遍再說。
“安全評估”那題,我一看是道圖論,我就直接放棄,反而是第二道題“砍怪”看起來相當(dāng)簡單。
我用大概15分鐘打完了一個暴力,由于題面過于簡單,我并沒有再去看題,等到我寫完之后,我才發(fā)現(xiàn):這題沒了?!
沒錯,這道題在我寫完之后莫名其妙消失了??赡苤皇墙M委會不小心多放了一道題,但卻浪費(fèi)了我的20分鐘。
上午,我在比賽開始后5分鐘才開始看PPC題(之前在看Crypto),結(jié)束之后,我認(rèn)為如果早點(diǎn)開始寫的話名次還能上升。。結(jié)果下午組委會就出了個大鍋,把我的心態(tài)搞崩了。。
當(dāng)時,那道最簡單的 Crypto 已經(jīng)有幾個人做出來了,我如果堅(jiān)持上午的策略,先看 Crypto ,再看 PPC ,也許可以比其他人快一些。
在心態(tài)受到打擊,之后又看了不到四分鐘另一道PPC后,我決定暫時擱置PPC,去寫其他題。
我瞄準(zhǔn)兩道做的人最多的 Crypto 和 Reverse ,當(dāng)時比賽開始了28分鐘,到比賽開始40分鐘時,我已經(jīng)完成了這兩道題,沖到了排行榜第7名。
整場比賽中做出來人數(shù)第二多的題,也是最簡單的一道 Crypto。
稍有密碼學(xué)經(jīng)驗(yàn)的同學(xué),看到下面的輸出里包含括號、下劃線、和分散的 字樣,就一定知道這是個柵欄加密。
我先是解讀了一下這個腳本,發(fā)現(xiàn)這個加密只改變了每個字符的位置,字符的集合是不變的。
于是我把腳本下載下來,用這個長度為25的字符串替代長度為25的flag加密,之后看每個字符移動到的位置,就可以通過密文反推出明文:
是比賽中通過最多的題,是一道,40位選手中,共有32人通過了這道題,80%的通過率,想想就相當(dāng)可怕。
其實(shí)只要有一點(diǎn)計算機(jī)常識,就可以做出來這道送分題,簡直是“有手就行”。甚至在我看來,比賽結(jié)束后計算md5值都比這個技術(shù)含量高。
直接下載附件,用記事本打開(我這里用的是linux上的Kate),就可以看到flag。
當(dāng)然,Windows下可能會用編碼錯誤導(dǎo)致無法查找flag,沒關(guān)系,把它放到IDA中也可以一眼看見flag。
這道題簡單到了什么程度,我上張圖的時間為38:45,這張圖的時間為39:40。也就是說,我在1分鐘內(nèi)提交了兩個flag。
去年、今年、初賽、復(fù)賽。我從來沒有在比賽中做過這種明文保存 flag 的 Reverse 題,這次這道題真的是一點(diǎn)難度都沒有。
做完這兩道題后,我處在第7名的位置,再看時間還多,也沒啥做題的動力,于是各種發(fā)呆和看題。
在這不到1小時的時間里,我把所有題看了個遍,能下載的附件都下載了回來,創(chuàng)建了一個又一個web和pwn實(shí)例,還幾次去看排行榜、PPC評測記錄和選手們的臉。。
1小時22分時,我的排名從7掉到了12。而這時我也把題看得差不多了,我意識到我只有完成那道“安全評估”,才能擠進(jìn)前16。
其實(shí)這道題我一開始以為是一道最小生成樹,于是在我做出那兩道水題后就在本機(jī)和書上找類似的題。我們這次比賽是允許使用本機(jī)資源的,也就是說,如果你做過這道題,或者你保存著這道題的題解,那么你可以直接提交。比賽開始5分鐘內(nèi)就有選手通過了這道題,估計他們用的就是這種方法。
我真的是對圖論一竅不通,什么都不會寫,于是我的策略就是拿別人類似題的代碼修改。在知道這并不是一道最小生成樹而是單源最短路徑的時候,我將目光轉(zhuǎn)向了Dijkstra算法。
我翻開了《算法競賽進(jìn)階指南》,并且在本地找到了與之匹配的代碼庫:
當(dāng)時我還沒有考慮什么堆優(yōu)化,我一直認(rèn)為這些優(yōu)化算法過于復(fù)雜,會導(dǎo)致出了問題很難排查。于是我把別人寫的dijkstra復(fù)制了下來。
簡單地改了改,主要體現(xiàn)在輸入格式的不同和對“路由器”設(shè)定的變動,這點(diǎn)后面再說。
總之,大約比賽開始2小時后,我提交了代碼。
提交之后,我意識到,這個規(guī)模的數(shù)據(jù)根本不能使用鄰接矩陣存儲,需要上鄰接表+堆優(yōu)化Dijkstra,于是我又復(fù)制粘貼了堆優(yōu)化的核心代碼,并且針對核心代碼改main函數(shù)。
說道這里,可以介紹一下這道題和它的解法了。
題目鏈接:https://mssctf.xidian.edu.cn/challenges#%E5%AE%89%E5%85%A8%E8%AF%84%E4%BC%B0-6
其實(shí)這題的建模過程并不難。題目大致就是給定n個點(diǎn)和m條帶權(quán)邊,并且有o個“路由器”,每個路由器都連接了數(shù)個點(diǎn),任意兩個連接著的點(diǎn)都存在一條權(quán)值為b的邊,求從s點(diǎn)出發(fā)的單源最短路徑。
這道題和一般的最短路模板題不一樣的是:增加了“路由器”這個設(shè)定。
我對這個設(shè)定的解決方法是:把每個路由器都看作是一個點(diǎn),每條與它相連的邊的權(quán)值為b/2。這樣,對于每個不是路由器的普通節(jié)點(diǎn),可以實(shí)現(xiàn)與題目描述相同的效果。
那么b如果不被2整除怎么半呢?用double嗎?
很簡單,直接把所有的邊權(quán)都×2,最后再除以2就可以了,根據(jù)題目描述,結(jié)果一定是整數(shù)。
這樣,這道題就成為了一道普通的單源最短路徑的模板題。
回到比賽,我在2小時5分之后就開始寫對于堆優(yōu)化代碼的main函數(shù),由于有特殊的數(shù)據(jù)結(jié)構(gòu),我只好“照葫蘆畫瓢”,但得益于我的代碼能力,我花了不到30分鐘改好了代碼,并且通過了樣例。在2小時35分時,我第一次提交了代碼。
然后它就RE了。。之后我又把數(shù)據(jù)規(guī)模又開大了一些,WA。
之后反復(fù)看了幾遍代碼,又調(diào)試了幾次,但并沒有發(fā)現(xiàn)什么問題。
當(dāng)時離比賽結(jié)束,只有不到20分鐘了。
現(xiàn)在看看這段的錄屏,都會覺得刺激。在比賽結(jié)束前4分鐘從19擠到16,簡直是太驚險了。
為什么我會把2小時40分鐘作為一個分界點(diǎn)呢?因?yàn)樵谀侵螅腋淖兞瞬呗?,開始瘋狂提交代碼。
由于我并不清楚準(zhǔn)確的問題,只能挨個去試。
當(dāng)時我精神緊繃,一直在看表,這份緊張也影響了我后面的發(fā)揮。
在2小時50分時,我把代碼中的int都改成了longlong,這下干脆連樣例都過不了了,還白白浪費(fèi)了時間,
在55分時,我發(fā)現(xiàn)了一個最大的問題:變量重復(fù)定義。我在for循環(huán)外定義了一個x,又在循環(huán)中定義了一個x用于計數(shù),還在循環(huán)內(nèi)修改了它。
我立馬把1分鐘前提交的沒有修改longlong的代碼拷貝下來,開始修改。
但由于我過于緊張,忽略了被注釋掉的freopen,導(dǎo)致我以為我的本機(jī)出了問題。
于是,我干脆直接在提交框里修改:
正當(dāng)我提交之后,驚喜出現(xiàn)了。彈出的并不是我期待的“Added to Judge Queue”,而是“You have already solved this”。
當(dāng)時的我真的驚呆了。
“你已經(jīng)解決了這道題”,因?yàn)槠脚_不能查看提交的具體代碼,我當(dāng)時并不知道我的哪次提交通過了這道題。直到現(xiàn)在,我才明白,重復(fù)定義并不會給程序造成什么影響,for循環(huán)內(nèi)外兩個變量應(yīng)該是相互獨(dú)立的,真正的罪魁禍?zhǔn)资牵涸u測機(jī)并不會忽略行末空格
當(dāng)時我修改代碼,去掉行末空格時,根本沒有報要AC的心態(tài),就是隨便改一改,OI系列比賽一般都是會忽略行末空格的,沒想到這個比賽沒有這樣做,賽后組委會還特別向那些栽在這個點(diǎn)上的選手解釋道歉,那些提交了幾十次沒有AC的選手應(yīng)該也是因?yàn)檫@個。
總之,這個在OI中沒有人關(guān)注的點(diǎn),竟成為了我解這道題的關(guān)鍵,其它的難度都不大,就看細(xì)節(jié)上的處理。
當(dāng)時,我第一反應(yīng)是去看排行榜,一看16名,真是太驚險了。
我長舒一口氣,靜靜地觀察排行榜,等待比賽的結(jié)束。
父母在看見我在比賽快要結(jié)束時還在第19名,就沒有再關(guān)注排名了。比賽結(jié)束后,我口中的16名,著實(shí)讓他們吃了一驚。
據(jù)一位看了B站排名直播的同學(xué)說,結(jié)束前4分鐘我沖進(jìn)了排行榜前16,還是最后一個拿到分的選手。
在我之后的PPC提交就全是RE和WA了,這也許就是心態(tài)的差距吧。
第二天的線上閉幕式,說好要辦一個小時,但最終只持續(xù)了20多分鐘。會上,我看到馮校長在現(xiàn)場,還是組委會邀請到的“知名中學(xué)的領(lǐng)導(dǎo)”的第一位,頓時心潮澎湃。
順便說下,這場閉幕式B站有錄播,傳送門:https://www.bilibili.com/video/BV1354y117rP
宣讀獲獎名單的時候,主持人差點(diǎn)把我給落下,這里吐槽一下。
最終,學(xué)校的一位老師替我拿回來了獎狀和大支票,我也在周二學(xué)校的信息課上見到了它們。
周四,我登上了學(xué)校公眾號,編輯還特意把我的名字放在了標(biāo)題上,文章在這里:https://mp.weixin.qq.com/s/jwPMs0CtaUzah5Di-07T3g
去年的比賽,我是第19名,差1分進(jìn)前16得獎,當(dāng)時的我并沒有堅(jiān)持到最后,早早地就放棄了。這次上午比賽,我也是第19名,下午在我做出來最后一道題之前也是19名,好在我堅(jiān)持到了最后,用對細(xì)節(jié)的追求和心態(tài)彌補(bǔ)了去年的遺憾。這種感覺是非常奇妙的,“history repeats itself”,我真的無法想象如果我沒有做出那道題,再次拿到第19名,之后會發(fā)生什么樣的事。
比賽結(jié)束后,獲得三等獎的喜悅很快就消散了,這次比賽的偶然性真是太強(qiáng)了。這次比賽中我最大的收獲并不是這個三等獎,也不是5000元獎金(雖然確實(shí)挺多的哈哈),而是這次獨(dú)特的經(jīng)歷。我第一次在比賽結(jié)束前4分鐘有突破性的進(jìn)展;第一次在最后時刻“臨危不亂”,“扭轉(zhuǎn)乾坤”;在我好幾次為了一點(diǎn)點(diǎn)微小的希望而去掉行末空格中,第一次真正的看到希望……這次比賽的經(jīng)歷真是奇妙啊。