1. 程式人生 > >JZOJ5966【NOIP2018提高組D2T3】保衛王國(並查集)

JZOJ5966【NOIP2018提高組D2T3】保衛王國(並查集)

題目

還是懶得把題目放上來了。
大意:給你一棵帶點權的樹,你要花費一些代價選擇一些點使得相鄰的兩個點至少有一個被選。
然後有很多個詢問,每個詢問強制兩個點的狀態,問強制了這兩個點的狀態後的方案。


比賽思路

沒時間了,沒時間了……
匆匆打個44分的暴力就好了。
結果混淆了概念,打出來的DP是求一個點自己或周圍至少有一個選的方案,和題目就不是一個樣子。
比賽結束了,我還沒有調處來,然後就爆0了。


解法

先說說暴力。
這是一個非常典型的問題,設 f

i , 0 / 1 f_{i,0/1} 表示以 i
i
為根的子樹中,不選或選 i i 的最優解。
(比賽時設的根本就不是同一道題)
這個DP的方程應該沒有人不懂的吧:
f i
, 0 = f s o n , 1 f i , 1 = min ( f s o n , 0 , f s o n , 1 ) f_{i,0}=\sum f_{son,1} \\ f_{i,1}=\sum \min\left(f_{son,0},f_{son,1}\right)

所以,暴力做法就是,將某一個值賦值為無限大,然後暴力地重新DP(當然,其實只需要更新它自己到根的這條路徑就好了)。

考慮正解。
首先說一下,我的解法是在JZOJ、洛谷排名第一的並查集解法,時間複雜度幾乎是線性的。倍增解法或許和並查集解法有很大相似之處,而對於那些打樹鏈剖分的動態DP的方法,個人認為我的方法和他們的方法存在著太大的差別。

首先我們可以抽象地思考一下:
對於這個詢問,其實就是將詢問的兩個點提起來,答案為限制了它們之後,鏈上的子樹的貢獻。
首先我們處理另一個DP,設 g i , 0 / 1 g_{i,0/1} 表示除以 i i 為根的子樹外,不選或選 i i 的最優解。(將它提起來之後,之前的父親也可以看成一個兒子。)
這個方程也是挺好想的(為了方便表達,設 f i , 2 = min ( f i , 0 , f i , 1 ) f_{i,2}=\min\left(f_{i,0},f_{i,1}\right) ):
g s o n , 0 = g i , 1 + ( f i , 1 f s o n , 2 ) g s o n , 1 = min ( g i , 0 + ( f i , 0 f s o n , 1 ) , g i , 1 + ( f i , 1 f s o n , 2 ) ) g_{son,0}=g_{i,1}+(f_{i,1}-f_{son,2}) \\ g_{son,1}=\min\left(g_{i,0}+(f_{i,0}-f_{son,1}),g_{i,1}+(f_{i,1}-f_{son,2})\right)
這個方程是從上往下轉移的。
其中 f i , 1 f s o n , 2 f_{i,1}-f_{son,2} 中,由 f f 的轉移方程得 f i , 1 f s o n , 2 = ( f s o n , 2 ) f s o n , 2 = s o n s o n f s o n , 2 f_{i,1}-f_{son,2}=\left(\sum f_{son',2}\right)-f_{son,2}=\sum_{son'\neq son} f_{son',2}
下面的那個類似。

這樣子DP部分就搞完了,剩下的東西就是維護。
我們可以參考一下Tarjan求LCA的過程(這個名字有毒,和強聯通分量的那個完全不是一個東西,不要被名字震撼到),其實也就是用並查集求LCA的過程。
簡要地說一下過程:
對於一個節點 u u ,首先 f a u = u fa_u=u ,然後dfs它的兒子。當從它的兒子那裡回溯上來的時候, f a s o n = u fa_{son}=u ,然後列舉和 u u 有關聯的詢問,設另一個點為 v v ,如果 f a v 0 fa_v \neq 0 ,則它還未被訪問過,先不理它;否則, L C A LCA 就是 g e t f a t h e r ( v ) getfather(v)
至於這個演算法是為什麼,其實隨便想一想就可以了。在dfs的時候,先到 L C A LCA ,再到 v v ,回溯上去,在 L C A LCA 處轉彎,再走到 u u 。由此可見,自 v v u u ,深度最小的地方就是它們的 L C A LCA (莫名其妙地想起了ST表求LCA),深度最小的地方也就是 v v 在並查集上的最遠祖先。

對於每個點,我們不只是記錄一下它在並查集上的父親,還要記錄一下它和他父親之間的答案。
每個節點的答案記錄 4 4 條資訊,表示父親