樹鏈剖分詳解

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

對於任意一株樹,我們記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)。

樹鏈剖分詳解

樹鏈剖分定義 只是把一棵樹拆成鏈來處理而已,即將樹上的某些段一起通過資料結構優化進行處理來降低複雜度。樹鏈剖分相關定義 重兒子 ve v 為 u 的子節點中ve 值最大的,那麼 v 就是 u的重兒子 將子樹中最長的那一條鏈一起處理來降低複雜度 輕兒子 u 除了重兒子的其它子節點。重邊 點 u與其重兒...

詳解樹鏈剖分

樹鏈剖分,顧名思義為將鏈剖開分成多條。當我們想要修改樹上一條路的值或求值時,我們暴力只能用乙個個修改,這是非常慢的。這時,我們就要想乙個辦法,資料結構?但是資料結構我們都需要連續修改,可是樹上路徑的編號是不連續的。於是我們想了乙個辦法。我們先定義 fa x 為x的父親 dep x 為x的深度 siz...

樹鏈剖分詳解

重兒子 對於每乙個非葉子節點,它的兒子中 以那個兒子為根的子樹節點數最大的兒子 為該節點的重兒子 ps 感謝 shzr大佬指出我此句話的表達不嚴謹qwq,已修改 輕兒子 對於每乙個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子 葉子節點沒有重兒子也沒有輕兒子 因為它沒有兒子。重邊 乙個父...