這次要學的是乙個聽起來很虛的東西
沒錯寫起來更虛
畢竟都是在虛的東西上面操作……
虛樹,顧名思義,就是一棵不真實的樹【大霧】
它可以對於一部分點儲存整棵樹的所有資訊,而對一部分點選擇忽略,這樣可以增加dp/點分治的效率
為了給大家乙個更好的例子(免得看不懂虛樹到底能幹嘛),我們先來看乙個題目:消耗戰
容易看出,這道題需要對每個詢問做樹形dp,然後每次在原樹上dp都有$o\left(n\right)$的時間開銷,m次詢問做完肯定tle
這時候就要用到虛樹這個資料結構了
考慮一組詢問,我們在樹上有一些點是」詢問點「,剩下的點不是
也就是說,其實我們只是需要對這些」詢問點「處理資訊,然後再把詢問點的資訊合併就好了
誒,等等,萬一詢問點之間不全都是父子關係怎麼辦?
那看來我們還要加上詢問點們的lca,也一起放到這課樹裡面
這樣,我們的新樹中就有這組詢問的所有詢問點,以及它們互相之間的lca了,然後這個點的數量級是$o\left(詢問中的點數\right)$級別的
我們看到,題目資料範圍裡說,所有的詢問點數總和不超過300000
ok!這樣dp就不會超時了!!
所以上面這一步中,我們找到了做題的思路:把虛樹建出來,在虛樹上dp
那麼,怎麼實現構建虛樹的過程呢?
這個好像有點麻煩,因為我們並沒有什麼方法能對於乙個點集合求出它們的附加lca,所以我們得換個角度下手
這裡的思路比較複雜,說出來也價值不大,所以就直接提供方法了
我們維護乙個單調的棧,這個棧裡的元素關於深度單調,也就是說我們棧裡面儲存一條從當前根開始的樹鏈。
先把所有的」詢問點「按照dfs序排個序,然後依次把它們加入棧中
每一次,我們設grand為當前待入棧節點和當前棧頂節點的lca
然後做這樣乙個迴圈:
如果當前的棧頂深度大於grand,我們就在棧頂和棧的第二個元素中間連一條虛樹邊,並把棧頂彈掉,直到棧頂的深度小於等於grand
此時讓grand和上乙個被彈出的元素連邊
這時再把grand加入棧頂,然後再把待入棧節點加進來
所有的」詢問點「都入棧了以後,再把棧裡的元素乙個乙個彈掉並連邊就好了
注意這個過程中,所有的加邊只在彈棧過程中進行,注意不要寫錯
這樣建出來的虛樹就是比較點數少的了,而且在求dfs序的同時我們還可以維護深度和子樹大小之類的資訊,後面在虛樹上依舊可以使用
不過,有的時候(例如上面的例題),某個節點一定不會作為詢問點,此時可以先把這個節點(比如例題中的一號)加入棧,作為虛樹的根
否則就需要在每一次新加入詢問點的時候判斷棧是否為空,如果是空的就要直接把這個節點入棧
這樣的方法,最後棧裡剩下的那個元素,就是虛樹的根了
當然,有的題目因為需要一些和dfs時確定的根節點(比如1號)有關的資訊,所以必須以一號作為根,這種時候就要在dp裡面判斷一下了
說了這麼多,其實虛樹的建法也大都和題目有關,因此還是要多看題
這裡放三道虛樹例題,分別對應上面那段講的三種情況
sdoi2011消耗戰
heoi2014大工程
hnoi2014世界樹
可以看到虛樹一般作為優化dp時間複雜度的一種手段,題目真正的精髓還在dp上
像世界樹這道題就dp非常噁心,寫起來賊難受......
總結一下,虛樹就是乙個優化樹的結構的資料結構【好繞啊】,一般結合dp或者點分治使用(好像比較少見點分治的)
當然可能有什麼結合樹上莫隊啊啟發式演算法啊之類的噁心題,但是見得不多就是了
實際上,虛樹這個演算法源於去除冗餘資訊的思維,它把多的、不需要的資訊整合在了虛樹邊上
這一點在世界樹那題裡面特別體現了出來
資料結構和演算法學習(10) 2 3 4樹
之前所提到的樹全部都是二叉樹,即每個節點有乙個資料項,每個節點最多有兩個子節點。有多個資料項和更多子節點的樹被稱作多叉樹,所要學習的2 3 4樹就是一種多叉樹,他的每個節點最多有四個子節點和三個資料項。2 3 4樹同紅黑樹一樣是平衡樹,他的效率稍差,但是程式設計更加容易 2 3 4樹名字中的2 3 ...
演算法學習 資料結構 李超樹
李超樹的具體實現過程 我們先將每一條線段都表示成點斜式,接下來用 k 表示斜率,b 截距。當我們插入一條線段 y kx b 的到區間 l,r 插入直線則是 inf,inf 時候,我們需要判斷這條線段是否可以更新這個這個區間的答案。我們記一條線段 s 為優勢線段,表示在這個區間 l,r 中的線段中,s...
省選演算法學習 回文自動機 回文樹
首先你得會manacher,並理解manacher為什麼是對的 不用理解為什麼它是 o n 這個大概記住就好了,不過理解了更方便做 pam 的題 回文自動機 palindrome automaton 是一類有限狀態自動機,能識別乙個字串的所有回文子串 它可簡化構建出回文樹 網上資料很多,不拿出來一步...