【題解】AGC007E | 二分答案 複雜度分析

寂静的海底發表於2024-03-28

首先考慮題目要求每條邊被經過兩次,這說明了我們進入一個子樹後一定會處理完子樹內所有的葉子後離開該子樹,否則子樹上端那條邊會進出至少兩次,即經過至少四次。所以這說明了子樹之間的獨立性:某個子樹在答案中一定是一個連續的區間,這引導我們從有根樹資訊自下向上拼接的角度考慮。

我們就可以將一個有根樹抽象成三部分:從根進入 \(in\),內部最大值 \(max\),離開回到根 \(out\)。內部的部分不會再發生改變,我們只關心它的最大值 \(max\),然後可以透過拼接兩個子樹離開-進入的資訊來得到父親的資訊:\((in_a,out_b,\max(max_a,max_b,out_a+in_b))\)

因為這個過程是可以倒過來走的,於是我們就只需要設定 \(f_{a,b}\) 表示進入左子樹的 \(a\),從右子樹的 \(b\) 離開的最大值的最小值即可。

於是我們透過記錄所有每個點所有 \((in,out)\) 對應的 \(max\) 就有了一個 \(O(\text{poly}(n))\) 的做法(取決於你咋搞,反正難以低於 \(O(n^2)\))。

考慮最佳化:若要記錄內部最大值是一件很浪費的事情,我們需要最小化最大的一次,又因為我們的過程涉及到了邊的加和,故肯定得考慮二分答案轉化為可行性問題,問題變為需要檢測答案 \(Dis\) 是否可行。

然後對於剛剛那個問題,我們便不再關心每個 \((in,out)\) 了,我們只關心每個 \(in\) 可以對應的 \(out\),且如果存在 \(in_1 \geq in_1,out_1 \geq out_2\),那麼 \((in_1,out_1)\) 一定沒用,於是我們可以透過維護一組單增的 \(in\) 及其對應的一組單減的 \(out\) 值來描述這個子樹的資訊。

考慮資訊的拼接,即需要合併兩組 \(A(in,out),B(in,out)\),合併的條件為若有 \(Aout_i + Bin_j \leq Dis\) 則有 \((Ain_i,Bout_j)\),因為 \(A\)\(B\) 都滿足隨著 \(in\) 增,\(out\) 減,所以可以按順序列舉 \(Aout_i\) 雙指標維護可行的 \(Bin_j\) 即可。因為過程是可以倒過來走的,我們再把所有 \((out,in)\) 也加入,最終再排序/歸併排序,去除掉沒用的就可以得到父親樹內的 \((in,out)\) 序列。

直接這樣做看上去複雜度是 \(O(\sum siz_i)=O(n^2)\) 的,但是考慮因為我們記錄的 \((in,out)\) 對中 \((in,out)\) 一者一定是左子樹或右子樹中的一個點深度,又因為我們保留的 \(in\) 單增,\(out\) 單減,所有這樣的 \((in,out)\) 最多隻有 \(2\times light_i\) 對,其中 \(light_i\) 表示 \(i\) 的輕子樹大小,所以複雜度是 \(O(\sum light_i)=O(n\log n)\),加上外層二分,若使用歸併時間複雜度 \(O(n\log ^2n)\),直接 sort 三隻 \(\log\) 也沒啥問題。

這裡是程式碼,實現使用了直接上傳 vector 的做法,由於累計向父親上傳的大小之和仍然不大於輕子樹大小,不會影響複雜度。


感覺這也不難啊為啥評了 *3900 .jpg

相關文章