樹鏈剖分原理和實現

樹鏈剖分原理和實現

 

理解

 

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

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

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

樹鏈剖分

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

算法中定義瞭如下的數組用來存儲上邊提到的概念:c++

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

 

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

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

 

首先定義如下數組:算法

 
  
  
  
  

 

算法大體須要進行兩次的DFS,第一次DFS能夠獲得當前節點的父親結點(faz數組)、當前結點的深度值(dep數組)、當前結點的子結點數量(size數組)、當前結點的重結點(son數組)windows

 

 
  
  
  
  

 

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

 
  
  
  
  

 

而修改和查詢操做原理是相似的,以查詢操做爲例,其實就是個LCA,不過這裏使用了top來進行加速,由於top能夠直接跳轉到該重鏈的起始結點,輕鏈沒有起始結點之說,他們的top就是本身。須要注意的是,每次循環只能跳一次,而且讓結點深的那個來跳到top的位置,避免兩個一塊兒跳從而插肩而過。數組

 

 
  
  
  
  

 

實戰

 

以這道題目爲例,能夠看出算法大體有兩種操做,分別是求任意兩個節點所鏈接的路徑和、極值,又或者是以任意一個節點做爲跟節點來求與子結點的路徑和、極值,而求區間和、區間極值正是線段樹所擅長的。數據結構

首先要構建線段樹:

 
  
  
  
  
相關文章
相關標籤/搜索