樹鏈剖分 學習筆記

2022-05-31 04:48:17 字數 1657 閱讀 1950

樹鏈剖分及模板

在樹上的兩點之間最短路上,或是某一子樹上完成一些整體的更新或求和的任務時,需要一些更快的演算法。樹鏈剖分的思想是,將樹轉化為鏈,也就是將二維轉化為一維,然後用我們熟悉的線段樹完成。

首先將樹進行剖分,一般常用的是輕重邊剖分,一遍dfs搞定。然後進行線段樹預處理dfs,並處理好top。然後建造線段樹,最後在操作的過程中利用top來完成更新或查詢。

1.dfs1——預處理

先給出一些定義:對於每乙個節點,統計它的各個子樹的節點個數,其中乙個節點個數最多的子樹稱之為重兒子,其餘為輕兒子。連線重兒子與自己的邊叫重邊,其餘叫輕邊。重邊所連成的鏈稱之為重鏈。

因此每個點都在且只在一條重鏈上。

在這一次dfs中,我們的主要任務就是統計出size

所以我們可以在dfs的過程中統計出重邊(重兒子),同時預處理出每個節點的深度,父親,與子樹中的節點個數。

int dfs1(int x, int father, int

d) }

return

size[x];

}

2.dfs2——剖分

我們現在要將這個數上的節點對映到線段樹上,因此每個節點一定要對應乙個線段樹上葉子的編號,這樣才能建立線段樹。

由於我們已經標記好了size,我們希望一條重鏈上的節點對應的線段樹的編號是連續的,這樣就可以方便地更新了。如何來完成?只需要先dfs重兒子下去,這樣時間戳自然就排好了。

另外,我們要計算top值。所謂top值也就是乙個節點所在重鏈中深度最小的那乙個節點。對於重兒子,top選擇繼承。對於輕兒子,top是它自己。由於每個點都在且只在一條重鏈上,所以每個點的top是唯一確定的。這個top有什麼用?到第四步的時候再說

void dfs2(int x, int

topf)}}

}

3.線段樹的建樹

不再贅述,請看這裡。

4.樹上操作

樹鏈剖分的重中之重。

(1)任意兩點之間最短路徑上的操作

樹上任意兩點之間的最短路是唯一確定,並且必定經過lca。

這回我們可以利用到我們的top值了。選擇兩點中top較低的那乙個點跳到top的上方,並且處理掉這一條被跳過的重鏈。然後在選乙個再跳……直到這兩個點在同一條重鏈上為止。最後再累積一下這一段即可。這樣我們就通過了一次次的對鏈的操作完成了對樹的操作。這就是我們為什麼要求top,並且要求一條重鏈上的編號需要連續——因為我們每一次跳過的鏈都需要用線段樹來更新或求解,這樣就方便而快捷。

為什麼是選擇top值較低的來跳呢?回到我們的目的來看,我們的目的是要讓這兩個點最終跳到同一條鏈上,來完成操作。而在這之前,他們都低於他們的lca。事實上,這就是在求解lca。跳到同一條重鏈時上方的那個點就是lca!所以樹鏈剖分也可以輕鬆求解lca(還不用打線段樹哦~)

inline void treeadd(int x, int y, int

z)

if(idx[x] >idx[y]) swap(x,y);

update(

1,n,1

,idx[x],idx[y],z);

}

(2)子樹上的操作

樹鏈剖分學習筆記

寫 又犯了很sb的錯誤,線段樹寫錯了。好像每次都會把r l 1寫成l r 1,然後就只有20分。寫的比較醜,壓了壓之後190行。基本上是我打過的最長的乙個模板了 然後簡單介紹一下樹剖吧。樹鏈剖分,就是把樹剖分成鏈,然後用資料結構來維護這些鏈,使得詢問 修改的複雜度達到o logn o l ogn 不...

樹鏈剖分學習筆記

樹鏈剖分 mod estc oder modestcoder modest code r如果你是重兒子,你就在重路徑上。如果你是輕兒子,暴力沿著祖先向上爬最多log nlogn logn 次就可以遇到重路徑。或者到根 而樹上操作基本就是找祖先 也許有人喜歡我的碼風 include include d...

樹鏈剖分學習筆記

前言 書上只講了重鏈剖分,菜雞也只會這一種,想看其他的是別想了。要會樹鏈剖分,首先你需要了解一些概念。我們把乙個節點的所有兒子節點中子樹節點數最大的稱為重兒子,也就是size最大的子節點。size的定義我在講換根dp時說過,因此不再贅述。對於每個節點的重兒子,我們用 son x 來記錄它,父親節點到...