樹鏈剖分詳解

2022-08-05 14:27:12 字數 1382 閱讀 2087

對於任意一株樹,我們記r.size表示以結點r為根的子樹中的結點總數,稱為r的大小。記r.next表示r的所有子結點中size屬性最大的一個結點(如果沒有子結點允許留空),稱為r的重孩子,而其餘子結點稱為r的輕孩子,與重孩子相連的邊稱為重邊,而與輕孩子相連的邊稱為輕邊。利用深度優先搜尋演算法可以在o(n)時間複雜度內計算所有結點的size和next屬性。

很容易發現通過將每個結點與其重孩子通過一條特殊的鏈條連線,那麼我們就在圖中建立了多條互不相交的鏈路,每個結點都處於一個鏈路中(鏈路可能只有一個結點),這些鏈路稱為重鏈。每一條重鏈中所有結點深度不同,我們稱深度最小的為該重鏈的源點,對應的我們為每一個結點賦予一個屬性src,用於記錄其所在重鏈的源點,而且很顯然同一鏈路中相鄰結點具有父子關係。這個過程可以以一次o(n)時間複雜度的深度優先搜尋完成。

之後我們將樹中的重鏈全部取出,並首尾相連,這樣我們就得到了一個連續區間,處於同一條重鏈中的結點緊密相連。當然我們不需要真的改變資料結構,我們只需要通過深度優先搜尋為每個結點分配唯一的索引index,並且同一條重鏈上的結點分配的索引緊密相連,之後利用這些索引以及線段樹(或者其他資料結構)進行修改和查詢權值的操作。

對於更新權值的操作,我們只需要取對應的結點的index屬性,並利用這個屬性去修改線段樹中對應的值即可,這裡的時間複雜度為線段樹的時間複雜度,由於每個結點對應一個索引,因此線段樹實際維護的是長度為n的區間,修改的時間複雜度為o(log2(n))。

對於統計兩點u,v之間的路徑的總長度,我們可以先計算u與v的最低公共祖先lca,之後求s(u,lca)+s(v,lca)-s(lca,lca)。其中s函式用於求某個結點到其某個祖先之間的路徑長度。對於s(x,y)的實現我們可以從起點x出發,如果x與y不在同一重鏈上我們則直接統計x與x.src的距離,這裡利用線段樹時間複雜度為o(log2(n)),同時讓x賦值為x.src.father,之後x必定不為空,理由是y是x 的祖先。之後重複上面過程直到x與y同處一條重鏈,之後利用線段樹高效計算x與y之間的距離,至此流程結束。總的時間複雜度顯然取決於從葉結點到根結點中重鏈數目,下面說明一些樹鏈剖分的命題:

命題1:對於具有父子關係的一對結點y與x,若x是y的輕孩子,那麼x.size證明:這是輕孩子的定義所可以直接得到的,y.size>=1+x.size+y.next.size>x.size*2。

命題2:從根結點到任意葉結點之間的輕鏈數目不會超過log2(n),重鏈數目不會超過log2(n)+1。

證明:由命題1得知,一條從根到葉結點的路徑上輕邊數目不會超過log2(n),且由於路徑上重鏈和輕邊是交替出現的,因此重鏈數目上限為輕邊數目加一,即log2(n)+1。

因此結合這兩個命題可以保證我們s(x,y)的時間複雜度為o(log2(n)*log2(n))。

樹鏈的建立只涉及到了深度優先搜尋,時間複雜度為o(n),而線段樹的在知道初始值時可以以o(n)時間複雜度建立,因此預處理的時間複雜度為o(n)。

樹鏈剖分 樹剖換根

這是一道模板題。 給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有一個給定的權值。下面依次進行 m 個操作,操作分為如下五...

note 樹鏈剖分

樹鏈剖分,是一種將樹剖分成多條不相交的鏈的演算法,並通過其他的資料結構來維護這些鏈上的資訊。 最簡單的例子就是 lca ,假設現在有一棵退化...

20190729 樹鏈剖分

樹鏈剖分真是難寫極了 你需要先熟練掌握深搜 線段樹 倍增。。。 都是不好寫的東西啊,一手滑就wa 一 簡介 樹鏈剖分通常用於維護靜態樹上路徑資訊的問題。樹鏈剖分的核心就是將數分為若干重鏈 輕鏈,然後把他們當做序列,按順序拼接起來,處理序列上的區間問題 二 相關量 fa x x 在樹中的父親 用於倍增...