长链剖分
一些定義
重兒子:子節點中 子樹深度 最大的節點。
輕兒子:除重兒子外的其它子節點。
重邊:從一個點到其重兒子的邊。
輕邊:從一個點到其輕兒子的邊。
重鏈:若干條相連的重邊形成的鏈。單獨的節點也看作重鏈。
那么,一棵樹就可以拆分為若干條極長的重鏈。
實現與重鏈剖分類似。
一些性質
1.所有鏈長度之和是 (O(n)) 級別的。
2.任何一個節點的 (k) 級祖先所在的鏈的長度大于等于 (k) 。
3.任何一個節點跳到根所跳的次數不會超過 (sqrt{n}) 次。
應用
1
題意即在線求樹上 (k) 級祖先。
考慮把 (k) 折半,假設得到的數是 (r) 。
根據上面的第 2 條性質 , (r) 級祖先所在的重鏈長度不短于 (r) 。
所以,我們只要預處理出每條重鏈上的所有點以及鏈頂的 1~鏈長 級祖先,
這樣,在知道 (r) 級祖先后,即可 (O(1)) 算出 (k) 級祖先。
但是,如果直接這么做,復雜度仍然是 (O(log n)) ,這樣的復雜度和倍增一樣,還是不夠優秀。
接下來,可以發現只要滿足 (r>k/2) 即可用上面的做法做。
考慮讓 (r=highbit(k)) , (highbit(k)) 即 (k) 在二進制下最高位所代表的值。
這樣,用倍增預處理后,即可得到一個 (O(n log n)-O(1)) 的做法。
2
先考慮一個 (O(n^2)) 的做法。
樹形 dp ,
設 (dis(i,j)) 表示 (i) 到 (j) 的距離。
設 (f_{i,j}) 表示滿足 (x) 在 (i) 的子樹中且 (dis(x,i)=j) 的 (x) 的個數。
設 (g_{i,j}) 表示滿足 (x) , (y) 在 (i) 的子樹中且 (dis(lca(x,y),x)=dis(lca(x,y),y)=dis(lca(x,y),i)+j) 的數對 ((x,y)) 的個數。((x
eq y))
考慮在掃到點 (i) 時的轉移:
(ans+=g_{i,0}) ,
(ans+=sum limits_{x,y in son(i),x
eq y} f_{x,j-1} imes g_{y,j+1}) ,
(g_{i,j}+=sum limits_{x,y in son(i),x
eq y} f_{x,j-1} imes f_{y,j-1}) ,
(g_{i,j}+=sum limits_{x in son(i)} g_{x,j+1}) ,
(f_{i,j}+=sum limits_{x in son(i)} f_{x,j-1}) 。
這些轉移畫個圖就可以理解了,而且并不是重點。
這樣直接做可以得到一個 (O(n^3)) 的做法,用前綴和優化即可變成 (O(n^2)) 。
考慮繼續優化這個算法。
長鏈剖分,對于重兒子,直接繼承它的值,對于輕兒子,它一定是一條重鏈的頂端,所以直接暴力往下合并。
這樣,算法的復雜度變為 (O(sum) 鏈長 ()) ,即 (O(n)) 。
可以用指針實現,比較方便。
總結:維護與鏈長,即深度有關的信息時,可以考慮使用長鏈剖分優化轉移。
3
比較顯然的貪心策略:每次找到一條最長的路徑,記錄答案后把路徑上的權值清零。
這個策略可以用長鏈剖分優化。
令鏈長為這條鏈上所有點的權值和。
可以發現,因為重鏈不重合,其實這個貪心就可以轉化為選鏈長最大的 (k) 條重鏈。
總結
- 上一篇: 笔记本CPU莫名锁频率0.39GHz?教
- 下一篇: 测试病毒网站(可上传exe测试)