虛樹

給出一棵樹.

每次詢問選擇一些點,求一些東西.這些東西的特點是,許多未選擇的點可以通過某種方式剔除而不影響最終結果.

於是就有了建虛樹這個技巧.....

我們可以用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壓入棧. 整個過程就是這樣.....


待更。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章