虛樹


虛樹

虛樹看起來很簡單的樣子。

事實上也的確很簡單。

我們先來知道一下虛樹是用來干什么的。

對於一個問題,我們知道他可以做樹型\(dp\)

\(dp\)的類型大致是給你\(k\)個關鍵點,而\(dp\)的結果與這些關鍵點有關系

\(m\)組詢問,需要你對於每組詢問進行回答。

並且有條件\(\sum k\)\(n\)是同階的。

如果每次對於所有點都做一遍\(dp\),復雜度達到了\(O(nm)\)

所以,我們需要把復雜度往\(\sum k\)上靠。

這樣,就有了虛樹。

我們仔細想想,每次\(dp\)的時候是否真的所有點都需要計算呢?

答案顯然是不。我們只需要計算那些對於答案有影響的點。

再來思考一下哪些點對於答案有影響呢?

關鍵點顯然是有影響的。

同時,\(dp\)的時候顯然需要合並答案,因此,關鍵點之間的\(LCA\)也是有影響的

所以,我們構建的虛樹包括兩種點:關鍵點,\(LCA\),還需要一個毫無關系的節點作為根節點來合並最后的答案。

因此,構建虛樹就相當於把這些點拿出來,然后按照原樹中的方式連接好就行了。

怎么做呢?

先考慮怎么求出\(LCA\),顯然不可能\(O(k^2)\)去求

事實上,我們只需要把關鍵點按照\(dfs\)序(\(dfn\))排序,然后把相鄰點的\(LCA\)求出來就好了

這樣子,我們最多可能拿到\(2k\)個點(所以注意一下數組的大小,是\(2\)倍)

現在考慮怎么構建。

把現在所有的點按照\(dfn\)排序,維護一個棧,棧中的點全部在同一條鏈上,節點的深度從棧頂到棧底遞減。

對於新加入的一個節點,檢查新的節點是否在棧頂節點的子樹中,

如果在,直接加入棧中,仍然滿足鏈的性質,並且棧頂就是當前點在虛樹上的父親。

證明?因為已經按照\(dfn\)排序,因此一條鏈肯定從上至下依次出現。

如果當前點不在棧頂節點的子樹中,證明當前棧頂節點的子樹中已經沒有虛樹中的點了

直接把它彈出來,繼續檢查即可。

這樣我們就構建出了虛樹了。

大致的代碼如下:

sort(&p[1],&p[K+1],cmp);
for(int i=K;i>1;--i)p[++K]=LCA(p[i],p[i-1]);p[++K]=1;
sort(&p[1],&p[K+1],cmp);K=unique(&p[1],&p[K+1])-p-1;
for(int i=1,top=0;i<=K;++i)
{
	while(top&&low[S[top]]<dfn[p[i]])--top;
	Add(S[top],p[i],0);S[++top]=p[i];
}

至於虛樹要怎么用???

那就因題而異了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM