題目大意:給出一棵 n 個節(jié)點且以點 1 為根節(jié)點的的樹,現(xiàn)在給出一個 k ,需要在樹上選擇 k 個關(guān)鍵點,使得 n 個點到達根節(jié)點的路徑上,出現(xiàn)的最近的關(guān)鍵點的距離的最大值最小,現(xiàn)在需要輸出 k 分別為 1 ~ n 時的答案之和
題目分析:首先理解題意,這個題目中有且僅由一個思維點需要轉(zhuǎn)換過來,那就是直接計算 k 值下的答案是很難計算的,但是我們可以在某個答案下,貪心去計算 k 的值
怎么理解上面這段話呢?對于某個答案 x 來說,因為 x 是題目中所有距離最大值的最小值,換句話說,任何一個點到達根節(jié)點的路徑上,相離最近的那個關(guān)鍵點的距離一定小于等于 x 的,這樣的話我們可以貪心去模擬,假設(shè) x 已經(jīng)確定了,可以先找到當前深度最深的那個節(jié)點,顯然如果想要讓這個節(jié)點距離最近的關(guān)鍵點的距離小于等于 x 的話,那么令其向上的第 x 個祖先設(shè)置為關(guān)鍵點就好了,當剛才的那個祖先被設(shè)置為關(guān)鍵點后,其子樹上的所有點也就都滿足條件了(因為最深的那個點都滿足條件了,那么其余的點顯然也是滿足條件的),依次類推,貪心去模擬就能計算出 k 的值了
現(xiàn)在我們需要選擇合適的數(shù)據(jù)結(jié)構(gòu)去實現(xiàn)上述操作,對于上段所述的功能一一對應(yīng)一下,子樹對應(yīng)著dfs序,有了dfs序就可以建立線段樹,這樣每次找深度最深的結(jié)點就可以用線段樹維護dfs序上深度的最大值,找向上第 x 個祖先,對應(yīng)著樹上倍增,然后就是枚舉答案了,顯然答案的可行范圍是 0 ~ n - 1 (當所有點都是關(guān)鍵點時,答案為 0 ,當只有根節(jié)點是關(guān)鍵點,且整棵樹退化為鏈時,那么答案就是 n - 1 ),對于每次枚舉的答案為 i ,貪心需要模擬的次數(shù)就為 n / i ,整體的時間復雜度就是?(調(diào)和級數(shù)),再加上線段樹更新需要的一層log,總的時間復雜度為 nlognlogn
注意一下,因為可能會出現(xiàn)不同的答案對應(yīng)著相同的 k 值,所以我們可以倒著枚舉答案,最后再正著維護一下答案的最小值就好了,因為根據(jù)貪心的策略,ans[ i ] 代表的就是當 k = i 時的答案,顯然在 ans[ i ] 的策略上再增加關(guān)鍵點的話,ans[ i + 1 ] 一定是小于等于 ans[ i?] 的,所以從前往后維護最小值是可行的
最后就是對于線段樹的更新,對于每個樣例我們只需要 build 一次線段樹即可,對于每個答案在更新線段樹的時候,記錄一下更新的位置,計算完答案后再原路恢復就好了,如果對于每個答案都重新 build 的話,時間復雜度就會退化成 n * n * logn * logn