樹鏈剖分用一句話概括就是:把一棵樹剖分為若干條鏈,然後利用資料結構(樹狀陣列,sbt,splay,線段樹等等)去維護每一
條鏈,複雜度為o(logn)
那麼,樹鏈剖分的第一步當然是對樹進行輕重邊的劃分。
定義size(x)為以x為根的子樹節點個數,令v為u的兒子中size值最大的節點,那麼(u,v)就是重邊,其餘邊為輕邊。
當然,關於這個它有兩個重要的性質:
(1)輕邊(u,v)中,size(v)<=size(u/2)
(2)從根到某一點的路徑上,不超過logn條輕邊和不超過logn條重路徑。
當然,剖分過程分為兩次dfs,或者bfs也可以。
如果是兩次dfs,那麼第一次dfs就是找重邊,也就是記錄下所有的重邊。
然後第二次dfs就是連線重邊形成重鏈,具體過程就是:以根節點為起點,沿著重邊向下拓展,拉成重鏈,不在當前重鏈上的節
點,都以該節點為起點向下重新拉一條重鏈。
剖分完畢後,每條重鏈相當於一段區間,然後用資料結構去維護,把所有重鏈首尾相接,放到資料結構上,然後維護整體。
在這裡,當然有很多陣列,現在我來分別介紹它們的作用:
siz陣列,用來儲存以x為根的子樹節點個數
top陣列,用來儲存當前節點的所在鏈的頂端節點
son陣列,用來儲存重兒子
dep陣列,用來儲存當前節點的深度
fa陣列,用來儲存當前節點的父親
tid陣列,用來儲存樹中每個節點剖分後的新編號
rank陣列,用來儲存當前節點**段樹中的位置
那麼,我們現在可以根據描述給出剖分的**:
第一次dfs:記錄所有的重邊
void dfs1(int u,int father,int d)
} }
第二次dfs:連重邊成重鏈
void dfs2(int u,int tp)
} 當然,由於題目有時候要求邊很多,所以最好不要用二維陣列表示邊,應用鄰接表或者鏈式前向星。
當然,這裡面有乙個重要的操作,那就是修改樹中邊權的值。
如何修改u到v的邊權的值呢?這裡有兩種情況:
(1)如果u與v在同一條重鏈上,那麼就直接修改了
(2)如果u與v不在同一條重鏈上,那麼就一邊進行修改,一邊將u與v往同一條重鏈上靠,這樣就變成了第一種情況了
那麼現在的關鍵問題就是如何將u和v往同一條重鏈上靠?這個問題此處我就省略了。
至此,樹鏈剖分原理基本分析完畢!
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...
樹鏈剖分原理與應用
樹鏈剖分這個演算法我看了好多大神們的部落格,才慢慢領悟,希望我寫的過得去 眾所周知,維護區間資訊的題目可以用線段樹高效實現。類似於區間這樣一維的結構,在樹上維護兩點間的資訊 比如樹上兩點之間的最大值 也可以用線段樹嗎?仔細想想,好像很難實現。因為線段樹維護的是鏈狀結構,而樹是一張圖。但我們想想,如果...
演算法入門 樹鏈剖分 輕重鏈剖分
目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...