【UOJ 84】水題走四方(DP)
題意
給定一棵 \(n\) 個點的有根樹。初始有 \(2\) 個人在根結點。每 \(1\) 秒內,每個人要麼走向某個兒子,要麼停在原地不動。當兩人都在某個結點上時,其中一人可以立即傳送到另一個人的位置上。最後要求每個結點都被至少經過一次,問總共花費的最小時間。
\(n \le 5\times10^6\)。
分析
先說一個樣例,可以卡掉若干個假演算法:有兩根長筷子粘在根上,且靠近根的一端有一些毛刺。答案應該是比一根筷子的長度多一點,而有些演算法會給出接近兩根筷子的長度。
考慮問題的子狀態:兩人位於同一點上。此時子樹外的點必然已經被遍歷過,我們只要考慮子樹內花費的最小時間。為了簡化問題,我們用逆向思維,考慮最多能省下多少時間。這樣我們預設初始方案的用時為整棵樹的葉子結點深度之和。
設 \(f_u\) 為子樹答案,\(e_u\) 為子樹中的葉子個數。我們列舉下一次集合的地點 \(v\),注意 \(v\) 不一定是 \(u\) 的兒子。那麼最優策略一定是:留一個人呆在 \(u\),另一個人把 \(v\) 子樹外的結點逛完後傳到 \(v\)。注意最後逛到的葉子深度越大越優,因為最後一次逛不用傳回 \(u\),而逛的時候之前乾等的人就可以開始向 \(v\) 走了。設 \(d_v\) 為 \(v\) 的深度,\(d_m(u, v)\) 為 \(u\) 子樹內、\(v\) 子樹外的最深葉子的深度。那麼可以寫出轉移式:
\[f_u = \max\{ f_v + (d_v - d_u)e_v - \max(0, d_v - d_m(u, v)) \} \]直接做是 \(O(n^2)\)
更強地,\(v\) 只需轉移到滿足 \(d_m(u, v) \ge d_v\) 的最深的 \(u\)。這個可以反證得到,假設 \(v\) 還會轉移給更淺的祖先 \(w\),那麼一定可以讓 \(v\)
那麼我們只要找到每個 \(v\) 對應的 \(u\) 即可。接下來的做法就五花八門了,我寫的是長鏈剖分。從下往上考慮一條長鏈,維護一個棧,每次把當前的 \(v\) 加入棧頂。當出現短兒子時,我們只要彈掉深度不超過某個數的 \(v\) 即可。最終棧裡還剩下一些點,那麼它們一定是轉移給鏈頂的父親。
因為要先算短兒子再算長兒子,我一開始直接寫了遞迴,然後就 MLE 了。因此要用計數排序預處理出 dfn,再非遞迴地算,這樣才能通過(口區)。