對於一株樹,若是咱們須要在一株n個結點組成的樹上作樹上DP,可是其中僅有m個結點須要特殊處理。顯然咱們能夠在原樹上作一次完整的樹上DP,其時間複雜度爲O(n)。可是有的時候m遠小於n,且這種請求可能不止發生一次,那麼咱們將沒法在給定時間內完成任務。算法
實際上每次DP真正涉及到的結點數並很少,其僅爲m個結點以及m個結點中任意兩個結點的lca。因此若是咱們能借助這些m個結點和結點對所指出的lca按照樹中的祖前後代關係重組一株新樹,樹的大小將大幅縮小,這樣就能完成任務了,而新創建的樹稱爲虛樹。排序
說一下流程:遍歷
1.首先咱們對樹進行先序遍歷,並按照結點訪問的順序記錄結點屬性dfn,dfn越小表示其在先序遍歷時越早被訪問。這部分時間複雜度爲O(n)。請求
2.以後咱們利用st或樹上倍增算法對樹進行預處理,這部分時間複雜度爲O(nlog2n)。時間
3.以後咱們處理全部的請求,對每一個請求創建虛樹,並在虛樹上作DP操做。
創建虛樹的流程以下:
1.選取全部特殊結點,並加入到列表中。
2.對列表按照dfn屬性進行從小到大排序。
3.將每一個列表中的特殊結點與與其左右相鄰的特殊結點之間的lca加入到列表中。
4.從新按照dfn屬性排序列表。
5.創建一個棧,將列表中dfn屬性最小(下標最小)的結點加入到棧中。
6.以後從前到後遍歷列表中的剩餘元素。彈出棧尾全部非當前遍歷元素祖先的結點,並將當前遍歷元素的虛樹父親設爲棧尾元素,以後將當前遍歷元素加入到棧尾。重複這個過程。
先說明創建虛樹的步驟3會將任意兩個特殊結點的lca加入到虛樹中。考慮a,b,c爲三個先後相鄰的特殊結點。很顯然lca(a,b)與lca(b,c)有祖前後代關係,由於兩者均爲b的祖先,且lca(a,c)=lca(lca(a,b),lca(b,c))。故利用傳遞性咱們能保證任意兩個特殊結點的lca都被加入到了列表中。
而第5步,咱們創建棧並將dfn屬性最小的結點加入到棧中,若棧尾元素不是當前遍歷元素的祖先結點,而當前遍歷元素必定處於棧中,則表示棧尾元素所表明的子樹已經創建完畢,咱們將其彈出則表示不會有新的結點追加到其後。(這都是dfn的性質)
很顯然對於m個特殊結點,咱們僅僅選出了擁有2m-1個結點,即咱們創建的樹相對於問題的規模是線性的。