牛客多校總帖子連結:https://ac.nowcoder.com/discuss/1295959
第一場:
名次:31
賽時:A B C H I J (6題)
賽後:D
A 題和 B 題是捆綁的,同樣的背景,開場過完簽到後發現 A 過了好多人。
一開始設計了個 dp,好像是 n 方的,因為題目條件非常簡單,隊友在想能不能最佳化。我感覺這題沒必要去想太複雜吧,就又仔細畫了畫,發現存在子序列的AND和,其實就是相當於全選嘛,選的越多AND越小,列舉多少數第一位是1。n方的題,但是好像核心部分是O(n)的,模數不一定是質數使得算組合數成了n^2遞推。
B 題變成了存在至少兩個,也就是說可以刪一個數也能使AND為0,一開始zxn想列舉那個刪掉的數的位置,我覺得這不是個能容斥的東西,事實上也確實不能列舉刪掉的數是哪個,因為它沒有任何位置特徵,隨便塞在哪裡都行。但是zxn又想到了一個妙妙的東西,就是我們 A 題已經做出來了至少一個的,那麼減去只有一個的,就是答案了唄,雖然現在看來比較顯然,但是當時能想到這個還是挺妙的,畢竟沒有 A 題鋪墊直接想 B 可能要想很久。
只有一個說明每個數都不能刪,都很重要,每個數都有自己獨特位置的0,我們把所有m-1個位置的0分給那些數,但是這樣會出現一個情況就是某些位置上有好幾個0,分配時該如何分呢。經過討論,發現可以列舉哪些列是隻有一個0,其他列至少要有兩個0,至少兩個0的列可以隨便填,只有一個0的列要覆蓋到所有行,也就是有序號的盤子放有序號的球的方案。
這就是個經典斯特林數的變種,雖然自己有點記不清那幾個斯特林數的具體定義了,不過這題隨機模數以及n^2複雜度告訴我們只要寫出遞推式即可。以後我會再抽時間把斯特林數筆記補齊。
然後到了令人頭大的環節,一開始寫的n^2log T了,於是預處理了一下變成了n^2,還是T,有點氣不過牛客的評測機(我們學校有些隊在後面的比賽也被卡常了)。後來把迴圈次數減小了,勉強卡進,不過WA了,一開始以為是改多了,直到最後半小時才發現m=1特判錯了,我們欽定的m-1個位置0分配,是至少要有0的,不然不對。
賽後牛客群裡有人討論到卡常的問題,大佬發來了一個玄學且有效的最佳化,我暫且稱為取模最佳化:
double dp;
inline ll mod(ll A) {
return A - (ll)(A / dp) * p;
}
雖然難以置信,但是真的快了!2800->1700
C 題和 D 題是捆綁的,C 是簽到,D 是把第二層的和改成了異或和,也就是要統計每一位上有多少字尾和是1,就變成了金牌題。
小武一開始攻了好久沒思路,去做毒瘤題J了,我和zxn把B改對之後,對這題也沒啥期望了,但是我們討論到一個有用的點,就是每當後面加入一個新數字,相當於把前面所有字尾和全部加一個數,再想求某一位是不是1,也就是求區間內有幾個數,這個可以把區間加改成移動零點,只在詢問時改變區間。這個思路貌似行得通,不過剩餘時間不夠了,也挺麻煩,腦子裡全是晚飯了。
正解有兩個比較妙的點,一是要求第 k 位為1的數字個數,就是 \(\mod 2^{k+1}\) 後值大於 \(2^k\) 的數個數。開 log 棵樹狀陣列維護模上每個2整數次冪後值域分佈。
二是把字尾和改為字首和的差,\(a_i+...+a_n\) 改成 \(s_n-s_{i-1}\),這樣前面的字首和值都是不用變的,不用更新,詢問時 \(s_n\) 的意義就是移動零點。這題就這麼做完了,不過移動零點之後值域分了好幾段,挺麻煩的說。
I 題是個鏡子題,一開始dirt率很高有點不敢開,不過在隊友的鼓勵下還是去寫了。
很重要的一點就是光線不會交叉,要麼是環要麼是鏈,維護起來比較方便。一開始我們以為求光線長度就行了,再讀題發現詢問是光線經過多少種不同的鏡子,所以需要記錄顏色進行預處理。
小武給出了一個非常簡單的寫法,就是每個牆向左向右看成兩個不同的邊,這樣光線經過的邊也不會交叉,且每條邊的顏色也清晰明瞭。贊,很順利地一遍過了。
J 題是左右搖擺撞牆資料結構,倆隊友討論了個分塊再二分的做法,雖然我沒參與此題不過我覺得很合理,複雜度是 \(n\sqrt n·logn\) ,一直覺得1s內能過很玄乎,不過沒別的想法了就硬上,還挺難寫,小武寫了兩個小時,全程剛這一題。可喜可賀的是最後竟然跑過去了。看了眼題解,分塊改成了線段樹,不過我們因為已經過了所以就沒仔細再研究了。
第二場
名次:60
賽時:A B C E H I(6題)
賽後:G
E 簽到找到結論了馬上過掉,實際上判斷一個數是不是2的整數冪只需要一個lowbit,我寫複雜了。
小武寫了C,我寫了H,H是經典的機器人區間指令,只需要map記錄每個位置有哪些時間在這裡就行。
簽到題寫完去看B,一張圖每次給出幾個點,求子圖最小生成樹,這題卡了我們有一陣子。後來小武提出了根號分治的做法,小於根號個點直接n^2的prim演算法,哇郎prim竟然真的有用處,省個log。而大於根號用克魯斯卡爾怎麼去log呢,發愁了一會兒突然發現這個log僅僅是預處理時的排序耗時,之後就是一條一條邊加了,O(n)的。
實際上寫的時候發現prim本身是n^2,但是判斷邊還是需要一個map的log,改成unordered快一點點但是還是T了。賽後jiangly在群裡有提到可以把這個log處理掉,但是當時這題卡了好久,頭昏腦漲實在是不想思考了,就學著去年濟南站的B題,把根號向克魯斯卡爾那邊偏移了一點,最後均攤過了,賽後也沒有過多管這道題。
之後我去攻 I 題,zxn和小武手玩 A,他們先做出了 A,我還在死磕 I。
A 題我沒參與,但是這種磚頭有個特殊的性質就是從邊界出發的線條一定會在邊界消失,中間不會斷掉或者有個rho的形狀。所以邊界上的線條數是固定的,中間的圈就是多出來的數量。
zxn他們WA了兩發之後改對了,我也剛好寫完 I ,一起交,兩邊都過了。然後G死活不會做,就下班了。
I 題磕了一個多小時,終於發現了最大的規律,就是如果我決心要拿走某個區間,那麼這個區間內比兩邊小的數可以直接改成兩邊的數,我只需要考慮中間更大的區間。
於是決心拿走的每個區間成了子問題,就是求每個這樣的區間最大的價值。從最大的數開始求,區間內有更大的數就dp,而無需管更大數的區間是具體怎麼取的,因為那是已經給求好的子問題。
G 題看了題解感覺好妙,原來這場兩道根號分治。
雖然這題數字大小僅有1000,但是質數的個數還是不足以狀壓,直接來一手神之分治,將31以內的數看成小質數,數量可狀壓,37以上的數看成大質數,每個數只能有1個大質數。所以按照大質數進行分組揹包,每個大質數的組需要選出偶數個數才能組成平方數。
\(f[0/1][i]\) 表示大質數個數奇偶,小質數奇偶狀態為 i 的總權值,每加入一個數滾動更新到 g 陣列,再讓 f+=g。在分組揹包中,我們把沒有大質數的數看成第一組,即大質數是1,1的數量無所謂奇偶,不過其他大質數都要是偶數,所以每走完一個分組,那些 \(f[1][i]\) 的狀態就不能再有貢獻了,要捨棄掉,全部置為零。
第三場
名次:78
賽時:A B D E J L(6題)
賽後:H
L 是個數獨有關的籤,小武寫了。B 是裴蜀定理。
A 是個很有意思但是賽後被好多人噴的題,模仿了去年瀋陽的小船過河。
但是這題不是dp,而是一個結論題。每次至少L人能開船,最多運R人,我和zxn討論了好久,把題意轉為了:小船先無償運R個人,然後每次要派L個人去來回一趟接R-L個人,每人花費兩點體力。
這個該怎麼分配體力呢,本來以為是個神仙問題,後來發現是腦筋急轉彎。這就是好比有 (n-R)/(R-L) 次降水,每次選擇 L 個位置降2點水,給你每個容器的高度問你能不能透過協調選擇的位置來裝下這麼多次降水。直接求和肯定不對,但是把每個容器的上限改到降水次數的上限,再次求和就是對的了,一個小結論吧,也挺好證的,這題就做完了,稱不上順利,我倆卡了好久,但是終於擺脫了坐牢。
在過 A 之前就已經會 D 了,D 也沒啥知識點,就是難寫,斷斷續續地寫了一會兒就過了。發現小武還在做 J,我過來問下情況,小武說需要再基環樹上倍增,他把基環樹分成了樹和環分開討論,特別難寫。我有點懵。
我問為啥不能直接在基環樹上倍增,他好像突然懂了,猶豫了一會兒就去改了。雖然浪費了一點腦容量和時間,不過還是順利過了。
然後是 E 題,一個不聽話的打字機,求最優策略能打出所有好單詞的機率。
轉到01trie上,手玩了一會兒,發現只需要考慮每個節點的分流問題就好,就是每當到了一個位置,我往 0 處走有 x 個好單詞,往 1 處走有 y 個好單詞,那麼最優策略是哪裡多就往哪裡走。把每個節點分流成功的機率乘起來就是最後的答案。
zxn發現這個問題可以dp遞推,就是把剛剛的情況分流成功機率設為 \(f[x][y]\),於是順利解決了這題。期間小武去看 H 了。
H 是求最小的矩形,小武發現了個n^2的神仙做法,就開始寫,我們覺得穩了,開始討論晚飯吃啥。
誰知道寫完後一交TLE了,又是一個純n^2的做法T了,我們和第一場一樣著急。
不過這次沒有第一場那麼幸運了,一直T到了比賽結束。賽後小武好像發現了個小規律最佳化了下,卡過去了,只能說有點遺憾吧。
題不是我開的,我也不想補了。
第四場
名次:73
賽時:A C F G H I J(7題)
賽後:B E
這場整體比較簡單,同校的隊 9 題了,杭電一隊甚至 AK。
隊友寫了I,我寫了G。A題題目名LCT,不過是個帶權並查集。H 和隊友猜了個gcd的結論過了,C發現是置換環,也很簡單。簽到題就已經五道了。
然後是F,每個輸入只有一個數,很快啊!還沒來得及閃,隊友就找到規律猜了結論交了一發,但WA了。
隊友認為這題想讓點數最小就是一條鏈,我發現不止一條鏈,只需要讓某些點全在另一頭就行了,然後 u 和 v 中間留一半的點,這樣總貢獻就是點數平方,至於非平方數是否要加一,或加二,我們手玩了下發現不一定讓中間和另一頭的點數相同,也可以是 \(a(a+1)\) 醬紫,u 和 v 中間是一條鏈,在鏈上插一個點可以讓貢獻加一個數或者減一個數,這個數奇偶性和鏈長有關,所以 \(a(a+1)\) 的分配方式可以保證奇偶。
討論了許久覺得天衣無縫了,但是還是WA,最後發現是1e18的數取sqrt炸精度了,哈哈,二分過了。賽後得知有個sqrtl。
J 題是小武在我倆討論F的時候一個人開的,我全程沒參與,不說了。
之後就去看 B 和 E 了,B 是有趣的拉箱子問題,由於碰到箱子後不能走箱子的那條路而是要找個次短路,我就去往圓方樹那裡想了,隊友討論了下發現可以把每條邊兩個方向當做點,邊之間的連線雖然很多,但是可以用字首和最佳化,還是O(n)的,拉箱子的步驟就相當於不能走回頭路。我聽了下覺得可行,就去看E了。
結果雖然 B 思路是對的,但是拋開細節不說,我們隊最後十分鐘終於找到錯誤改對了,卻又因為卡常T掉了。雖然是個理論O(n)的演算法,但是邊數點數有點太多了,多了個字首和最佳化後又多了一倍的邊和點,牛客評測機有點吃不消。遺憾離場。
我這邊雖然把 E 轉化了題意,但是最後是個1e5的01揹包,我卻不會了,我想到高中時候見神魚有寫過多項式最佳化揹包的技巧,不過我沒學過,亂搞一通沒有過。後來看題解小武說他會01揹包的最佳化。。。如果讓他來寫估計就過了有點事後諸葛亮,因為他全場在調 B 也沒時間顧我這邊。
所以這次比賽的結果就是雙卡。
B 題小武還是不捨得修改他的字首和最佳化程式碼,我去看了眼題解做法,是按邊BFS,佇列裡存的是邊的編號,雖然聽起來沒啥區別,但是有些小細節。
除了小細節以外,如何最佳化菊花圖邊到邊之間遍歷的複雜度,題解有個非常妙的最佳化:每個點之會被vis兩次,第三次沒用。這是因為第一次vis到這個點,除了來邊,其他邊的dis值肯定都更新了,第二次vis這個點可以更新第一次的來邊,之後第三次就不會更新任何邊了,所以即使是菊花圖的中心點,它周圍的邊最多隻會遍歷兩邊,保證了複雜度還是O(n)的。
E 題的揹包最佳化問題我賽後補上了部落格,只能說大佬熟知的典題和套路題我沒見過是真的不會。二項式相乘的題,很多都可以用先ln再exp的技巧,而ln則是麥克勞林公式手動展開,無需費時間,只能說數學能力是ACMer一個很重要的木桶板。
E 題其實除了不會最佳化揹包外,我還犯了另一個很致命的錯誤,就是我魔改的kmp複雜度假了。這題雖然只有一個字串,但是我需要求出所有位置往後延伸一個錯誤字元後的失配位置,我選擇是暴力跳nxt陣列,這個其實字元全部相同就已經卡滿了,但是我想當然以為複雜度是對的就沒管。(後來得知包大爺死在了同一個位置哈哈,當時他還在群裡問為什麼他的exp板子這麼慢)
正確的做法一個是建AC自動機,最保險,xxx隊就是這麼寫的,另一個做法是魔改kmp到底,在nxt陣列基礎上再加一個 \(dp[i][0/1]\) 表示第 i 位下一個字元正確與否可以匹配多長的字首。不過這個魔改做法泛用性還是沒AC自動機做法好。
第五場
名次:36
賽時:B E H J L(5題)
賽後:
難度梯度最畸形的一場,四題隊伍從 70 多名一直到 850 多名,全靠罰時。
前期我們隊還是相當順利,L 題找到結論10分鐘就過了,小武寫E去了,我和zxn討論B,發現也是相當水的簽到,半小時就過了三道題。
第四道題是個爆搜,在找結論無果後,小武覺得符合條件的鏈數量其實不多,況且我們有最優性剪枝,我就開始上手寫,用到了之前CF1804E相同的鄰邊狀壓技巧,恰好跑進了1s。
算上一發dirt,前四題只有156的罰時,在4題隊裡也是相當靠前的。那一發超時提交是怎麼會事呢?
一開始以為是爆搜複雜度不對,於是我開始試隨機資料,結果30的時候能秒出,而34就完全跑不出來了,我們都覺得有點不正常了。結果最戰犯的一集來了,這題的 n 取值是40,而我程式碼裡狀壓的部分有一句 1<<n 直接炸了,改成 1ll 就過了,被隊友揍了一頓後長記性了,以後再也不犯這種zz錯誤了。
然後開始終極坐牢。
J 題知道有個prufer序列,但是第3個限制誰頂得住啊。我發現資料範圍甚至可以n^3,於是看看有沒有突破口。
想到了一個統計的方式,第一層列舉 1 到 k 的鏈上有多少其他的點,然後再將其他點掛在任何點上面。
1 到 k 的鏈本身有 \(\frac {k!}2\) 種不同情況(算上翻轉),將其他的點加在鏈上,就是個組合問題。至於其他掛著的點,我設計的是每一棵子樹作為一個子問題,比如在 1 點掛 i 個點,把 1 強制設為子樹的根,然後就是一個可以呼叫prufer定理的東西,這個也沒有好寫的式子,限制了prufer序列中每個數出現次數,其實就是個超組合數問題,我寫了個分母帶階乘的類似指數型生成函式的式子預處理了所有子樹大小的方案。(因為強制選了一個點作為根,它的度數要比其他點少1或2,取決於它是否是鏈首尾)
設有 i 個點插進了 1 到 k 的鏈上,之後就是把 (n-k-i) 個數掛在這個鏈上。分配每個點子樹大小,也就是 (n-k-i) 個位置 (k+i) 個顏色的超組合,同樣可以寫成指數型生成函式的式子。
因為 exp 的首項不是 0,多項式快速冪選用了雙 log 寫法,\(n^2 log^2\),卡過去了。題解好像是沒有 log 的揹包寫法,但和我的大體思路一樣,我用多項式純粹是大炮打蚊子硬套公式了。
雖然大概是兩個半小時的時候就寫完了 J 的程式碼,不過最後半小時才改對,講一下這題除錯時的曲折經歷。
因為細節非常多,除了這個雙層列舉的一般情況,我還特判了很多 d 和 k 比較小的情況,d 或 k 比較小都會影響到我式子的正確性。導致我程式碼能自測的最小樣例是 5 3 3,這個樣例一開始我程式碼跑了84,而 5 3 4 竟然也跑了個84出來,這很顯然不對,要麼是5 3 4算多了,要麼是5 3 3算少了。而我和zxn手搓竟然好久搓不出到底是哪裡錯了,手搓了好幾遍倆都算的84。。。無奈趕緊讓小武寫個暴力,雖然暴力也很難寫,不過小武硬著頭皮去寫了。
兩份程式碼buttle了一陣兒,暴力程式碼終於改對了,小武暴力跑5 3 3跑了108,可我還是不知道漏了什麼。這時大概還有一個半小時結束,這題驗證小樣例竟然驗證了我們一個多小時。我很生氣地決定把84種情況全都寫下來,畫到紙上,我畫5 3 3,zxn畫5 3 4,我倆開始快樂植樹,植了滿滿一頁,然後開始對照。
對照了一會兒終於找到了問題,我這裡有沒畫出來的情況,而我漏掉的邏輯和我程式碼裡漏寫的一個因子竟然完全吻合,不虧是我自己寫出來的程式碼。就是在 i 個點插入鏈內的時候,我只考慮了鏈上的位置關係,卻沒乘上 \(C(n-k,i)\) 表示到底選哪 i 個數。
改了之後,一切都通暢了。五道題完全可以下班了,我們開始討論晚飯吃啥。
黃大爺他們隊過了 G,賽後也把 J 過了,我只能說很神仙,不過我們那天實在太累了,G 也是看都沒看,就沒賽後補題。