【前言】
本文僅為本人學習樹鏈剖分的理解和總結,有誤之處請大佬指點迷津。也有與其他部落格不同或矛盾之處。
【樹鏈剖分】
顧名思義,將樹結構,剖分成鏈狀結構,然後將一條條的鏈拼接成線性結構,然後就可以通過線段樹、樹狀陣列等維護了。
說白了就是在樹上,有些值不好維護,通過樹鏈剖分轉化成乙個序列,而序列就好維護了。
樹鏈剖分做的事情就是把樹對映成乙個序列,讓線段樹去維護這個序列,以達到維護樹的目的。
【剖分方法】
1.概念
重兒子:結點u的子結點中,擁有最大子樹的那個兒子結點。
輕兒子:除了唯一的重兒子,其餘都為輕兒子
重鏈: 從結點u向重兒子延伸下去,所形成的一條鏈
輕鏈: 去掉重鏈,剩下的那些鏈都是輕鏈,一般來說,輕鏈都是孤立的點。
圖中粗邊連線的鏈就是重鏈,其餘為輕鏈。標記紅點的點為重鏈的起點。邊上的數字表示dfs時的順序(一定要重鏈的dfs序連續)。
2.設定陣列
陣列名功能
dep[u]
樹上結點u的深度(根結點為0)
dad[u]
樹上結點u的父結點(假設根結點為本身)
siz[u]
樹上結點u的子樹結點數目(包括u本身)
son[u]
樹上結點u的重兒子
top[u]
樹上結點u的重鏈隊長,即u所在的重鏈以top[u]為起點
rk[u]
樹上結點u對應的序列位置下標
id[ i ]
序列上位置 i 對應的樹上結點編號(基本無實際用處)
3.求出2中的所有陣列
兩次dfs,第一次dfs,求出dep,dad,siz,son,這四個很簡單。第二次dfs求top,rk,id,這裡需要注意dfs優先進入重兒子。
其實rk陣列才是主角,我們按照dfs進行的順序,給樹上點編號,並且讓重鏈的編號都是連續的(dfs2優先進入重兒子的原因),這樣我們就能用線段樹維護這段重鏈了。
/************ 樹鏈剖分 **************/
int dep[max],dad[max],siz[max],son[max];
int top[max],rk[max],id[max];
void dfs1(int u,int pre)
else
} //迴圈結束時,fu-fv在同一鏈
if(dep[u]>dep[v])swap(u,v);
update(rk[u],rk[v],num,1,1,limit);
}
【練習題】
hdu3966
題解:
樹鏈剖分學習
樹鏈剖分 看了學習了樹鏈剖分 適用於在樹上的路徑操作。關鍵在於重鏈的構造,把它表示到了資料結構上的連續區間,降低了複雜度。主要操作步驟實際上有三個部分 1 構造重鏈 2 如何維護資料 3 分解為輕重鏈的查詢與修改 include include include include define n 10...
樹鏈剖分學習
之前寫過樹剖的題,但沒有完全理解,現在又無法複述思路了,所以重新學習一下,部分語句參考大神的敘述 一 概念 樹鏈剖分,顧名思義,樹是由一根根樹鏈組成的,我們現在要來把它按鏈來分解掉。我們知道,大部分樹上的問題都是圍繞樹的路徑來做文章,分解成一條條的鏈之後,我們就可以對節點 邊 就行編號了,而同一根鏈...
樹鏈剖分學習
樹鏈剖分其實就是把一棵樹剖分成一條條鏈 重鏈輕鏈 然後用線段樹進行操作維護。複雜度logn 記錄 deep size fa pos bl 陣列 deep深度,size子樹節點個數,pos節點編號,bl所在重鏈頂端節點編號,fa父親編號。例題 bzoj1036 include define inf 1...