給出一棵樹.
每次詢問選擇一些點,求一些東西.這些東西的特點是,許多未選擇的點可以通過某種方式剔除而不影響最終結果.
於是就有了建虛樹這個技巧.....
我們可以用log級別的時間求出點對間的lca....
那麼,對於每個詢問我們根據原樹的信息重新建樹,這棵樹中要儘量少地包含未選擇節點. 這棵樹就叫做虛樹.
接下來所說的"樹"均指虛樹,原來那棵樹叫做"原樹".
構建過程如下:
按照原樹的dfs序號(記爲dfn)遞增順序遍歷選擇的節點. 每次遍歷節點都把這個節點插到樹上.
維護一個棧,它表示在我們已經(用之前的那些點)構建完畢的虛樹上,以最後一個插入的點爲端點的DFS鏈.
設最後插入的點爲p(就是棧頂的點),當前遍歷到的點爲x.我們想把x插入到我們已經構建的樹上去.
求出lca(p,x),記爲lca.有兩種情況:
1.p和x分立在lca的兩棵子樹下.
2.lca是p.
(爲什麼lca不能是x?因爲如果lca是x,說明dfn(lca)=dfn(x)<dfn(a),而我們是按照dfs序號遍歷的,於是dfn(a)<dfn(x),矛盾.)
對於第二種情況,直接在棧中插入節點x即可,不要連接任何邊(後面會說爲什麼).
對於第一種情況,要仔細分析.
我們是按照dfs序號遍歷的,有dfn(x)>dfn(p)>dfn(lca).
這說明什麼呢? 說明一件很重要的事:我們已經把lca所引領的子樹中,p所在的子樹全部遍歷完了!
(如果沒有遍歷完,那麼肯定有一個未加入的點h,滿足dfn(h)<dfn(x),我們按照dfs序號遞增順序遍歷的話,應該把h加進來了才能考慮x.)
這樣,我們就直接構建lca引領的,p所在的那個子樹. 我們在退棧的時候構建子樹.
p所在的子樹如果還有其它部分,它一定在之前就構建好了(所有退棧的點都已經被正確地連入樹中了),就剩那條鏈.
如何正確地把p到lca那部分連進去呢?
設棧頂的節點爲p,棧頂第二個節點爲q.
重複以下操作:
如果dfn(q)>dfn(lca),可以直接連邊q->p,然後退一次棧.
如果dfn(q)=dfn(lca),說明q=lca,直接連邊lca->p,此時子樹已經構建完畢.
如果dfn(q)<dfn(lca),說明lca被p與q夾在中間,此時連邊lca->q,退一次棧,再把lca壓入棧.此時子樹構建完畢.
最後,爲了維護dfs鏈,要把x壓入棧. 整個過程就是這樣.....
待更。。。