樹鏈剖分原理和實現


樹鏈剖分原理和實現

 

理解

 

樹鏈剖分就是將樹分割成多條鏈,然后利用數據結構(線段樹、樹狀數組等)來維護這些鏈。

首先就是一些必須知道的概念:

  • 重結點:子樹結點數目最多的結點;
  • 輕節點:父親節點中除了重結點以外的結點;
  • 重邊:父親結點和重結點連成的邊;
  • 輕邊:父親節點和輕節點連成的邊;
  • 重鏈:由多條重邊連接而成的路徑;
  • 輕鏈:由多條輕邊連接而成的路徑;

樹鏈剖分

比如上面這幅圖中,用黑線連接的結點都是重結點,其余均是輕結點,2-11、1-11就是重鏈,其他就是輕鏈,用紅點標記的就是該結點所在鏈的起點,也就是我們👇提到的top結點,還有每條邊的值其實是進行dfs時的執行序號。

算法中定義了以下的數組用來存儲上邊提到的概念:

名稱 解釋
siz[u] 保存以u為根的子樹節點個數
top[u] 保存當前節點所在鏈的頂端節點
son[u] 保存重兒子
dep[u] 保存結點u的深度值
faz[u] 保存結點u的父親節點
tid[u] 保存樹中每個節點剖分以后的新編號(DFS的執行順序)
rnk[u] 保存當前節點在樹中的位置

 

除此之外,還包括兩種性質:

  1. 如果(u, v)是一條輕邊,那么size(v) < size(u)/2;
  2. 從根結點到任意結點的路所經過的輕重鏈的個數必定都小與O(logn);

 

首先定義以下數組:

 
  
  
 
 
         

 

算法大致需要進行兩次的DFS,第一次DFS可以得到當前節點的父親結點(faz數組)、當前結點的深度值(dep數組)、當前結點的子結點數量(size數組)、當前結點的重結點(son數組)

 

 
  
  
 
 
         

 

第二次DFS的時候則可以將各個重結點連接成重鏈,輕節點連接成輕鏈,並且將重鏈(其實就是一段區間)用數據結構(一般是樹狀數組或線段樹)來進行維護,並且為每個節點進行編號,其實就是DFS在執行時的順序(tid數組),以及當前節點所在鏈的起點(top數組),還有當前節點在樹中的位置(rank數組)。

 
  
  
 
 
         

 

而修改和查詢操作原理是類似的,以查詢操作為例,其實就是個LCA,不過這里使用了top來進行加速,因為top可以直接跳轉到該重鏈的起始結點,輕鏈沒有起始結點之說,他們的top就是自己。需要注意的是,每次循環只能跳一次,並且讓結點深的那個來跳到top的位置,避免兩個一起跳從而插肩而過。

 

 
  
  
 
 
         

 

實戰

 

以這道題目為例,可以看出算法大致有兩種操作,分別是求任意兩個節點所連接的路徑和、極值,又或者是以任意一個節點作為跟節點來求與子結點的路徑和、極值,而求區間和、區間極值正是線段樹所擅長的。

首先要構建線段樹:

 
  
  
 
 
         

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM