一.虛樹的定義與作用.
虛樹:在一棵n
nn個點的樹上,基於某k
kk個點與它們兩兩之間lca構造的一棵新樹滿足這k
kk個點之間的輩分關係與原樹一樣,這種樹稱為虛樹.
可以證明虛樹的節點個數不會超過2k−
12k-1
2k−1
個.虛樹解決的問題一般會對一棵n
nn個點的樹進行多次的詢問,每次詢問只會用到樹上k
kk個點的資訊,並且k
kk的總和不超過乙個數值.
這類問題的乙個特性是若每次詢問都大力在原樹上得出答案會使得複雜度與n
nn同級,但若構建出原樹基於需要用到的k
kk個點構造的虛樹上得到答案則會使得複雜度變成與k
kk同級,能夠大大減少不必要的遍歷節點.
二.虛樹的構造.
現在我們來看虛樹的構造.
在構造一棵虛樹前,我們先對原樹跑出每乙個點的dfs序,然後把k
kk個點按照對應dfs序大小排個序.
排完序後,我們列舉這k
kk個點.假設我們現在列舉到了第i
ii個點,並且用乙個棧維護了第i−1
i-1i−
1個點到原樹根上所有的在虛樹上存在的點,並構造出了前i−1
i-1i−
1個點的虛樹.
現在我們考慮如何構造前i
ii個點的虛樹,顯然我們最多隻會增加兩個點,乙個是第i
ii個點本身,另乙個第i
ii個點與第i−1
i-1i−
1個點的lca,並且我們要同時把維護鏈的棧更新好.
這個時候我們考慮求出第i
ii個點與第i−1
i-1i−
1個點的lca,先把棧中這個lca的所有後代去掉,並判定這個lca是否在棧中,若不在則加入棧中,然後在棧中加入第i
ii個點.
至於連邊,我們需要在每一次去掉乙個點的時候才能連這個點與它父親之間邊.
這樣我們就可以完成構造啦.
至於連邊的邊權什麼的就要看具體的題目了.
設要多次建立虛樹總點數為k
kk,構造虛樹的時間複雜度為o(k
logn)
o(k\log n)
o(klogn)
.還有一點,建虛樹的時候一般可以把根也放到關鍵點中.
**如下:
//假設虛樹上的邊權是原樹上這條鏈的邊權最小值
//tag為1表示這個點時原來的關鍵點,為0表示是關鍵點的lca
int ca,a[n+9]
,lca[n+9]
,tag[n+9]
;int sta[n+9]
,cst;
bool cmp
(const
int&a,
const
int&b)
void
build()
for(
int i=
1;i<=ca;
++i) tag[a[i]]=
1;for(
int i=
1;i<=ca;
++i)
if(a[i]
^sta[cst]
) sta[
++cst]
=a[i];}
for(
;cst>1;
--cst)
ins(
1,sta[cst-1]
,sta[cst]
,query_min
(sta[cst]
,sta[cst-1]
));}
更詳細的**參見bzoj2286.
三.樹鏈的並.
樹鏈的並:在一棵n
nn個點的樹上有k
kk個點a
ia_i
ai,現在要求精確覆蓋根到每個a
ia_i
ai的鏈(也就是說每個在樹鏈並上的點只能被覆蓋一次).
這個問題其實很簡單,用類似虛樹構建的方式維護即可.
比如說我們現在要把k
kk個點a
ia_i
ai為基礎的樹鏈的並上所有點點權+1+1
+1,就可以先把a
ia_i
ai按照dfs序排序,然後列舉每個點a
ia_i
ai,把a
ia_i
ai到a
ia_i
ai與ai−
1a_
ai−1
的lca這條鏈都+1+1
+1,大力用資料結構維護即可.
具體應用可以參見bzoj3881.
虛樹入門筆記
前置芝士 lca,dfs序 參考內容 虛樹 洛谷 虛樹 實際上是同一篇 lh 對於一棵給定的 n 個節點的樹 t 構造新樹 t 使其包含指定點集及它們的 lca,且節點數最小。這樣的 t 被稱為虛樹。虛樹可以解決樹上對於指定點集 s 的詢問,時間複雜度可以實現 mathrm 其中 f 為樹上解決該問...
樹鏈剖分入門
例題 poj 3237 bzoj 3083 bzoj 3531 bzoj 3589 bzoj 3626 將樹上問題通過dfs序的性質轉換為區間問題,從而對樹上修改時就可轉化為相應的區間修改。再通過引入重兒子,重鏈一系列概念將時間複雜度也變成了可以接受的層次 通過兩次dfs求出樹上一系列的資訊 voi...
樹鏈剖分入門
我學的學習資料 和 ppt 樹鏈剖分可以解決很多問題,輔助一些線段樹之類的資料結構可以解決一些樹上修改的問題。還可以求lca,不過複雜度比rmq實現的lca多乙個log。下面是樹鏈剖分實現的lca 1 const int maxn 5e4 10 2 struct data edge maxn 1 5...