將關鍵點按dfs序排序後,所有關鍵點與相鄰關鍵點的lca合起來構成虛樹(通常還要加上整棵樹的根)。
虛樹至多有2k2k
個點。體現在實現中就是每次都pop
若干點,並有機會push
2個點。
stk
中存的是從根到當前點的遞迴棧中目前選入虛樹的點。
stk
中的點之間都未連邊(因為事實上關係還未確定),pop
掉乙個點的同時把它的父邊連上。
插入乙個點時,計算這個點與上乙個點的lca,然後分類討論:
當前點是上乙個的子節點,直接把自己push進去
否則根據top-1
判斷是否彈出top
,直到出現第3類情況(top-1
的深度
>
>
lca的深度)
a. 需要pop當前棧頂並push lca,注意lca是剛彈出的點的父親
b. lca就是當前棧頂,通常不需要做什麼
然後把自己push進去
初始時,棧中包含(硬點
a[0]
),然後依次加入a[1]
到a[n-1]
。
root
前面還要新增保護節點stk[0] = 0
,並設定dep[0] = 0, dep[root] = 1
,避免在棧中只有乙個root
時過度彈棧。
initialization:dfs處理in[maxn], dep[maxn]
,查lca用的st表以及adde
時要用的一些資訊
int a[maxn], stk[maxn] = ;
inline bool cmp(int u, int v)
void rebuild(int k)
do adde(*top, *(top-1));
while (--top > stk + 1); //clear stack (don't forget!)
}
虛樹上dp時,可以直接在棧中記錄dp值,因為stk
本身就是虛樹上dfs的遞迴棧。
把adde
換成pushup
即可。注意棧中的兒子還未向父親pushup
,彈棧時才pushup
。
val_t solve(int k)
do dp[top-1].pushup( dp[top].finish() );
while (--top > 1); //clear stack (don't forget!)
return dp[1].finish();
}
為了演示如何將樹形dp改寫為棧中dp,約定樹形dp採用如下模板形式
class val_t;
class dp_t dp[maxn];
void dp_t::upgrade(int fa)
val_t dfs(int x)
虛樹 學習筆記
水平不夠,學習來湊 又開了個天大的新坑 sdoi 2011 消耗戰 題目大意就是講 給出一棵樹,有邊權,然後給出k個查詢點,問從1號店不能到任何乙個查詢點的代價是多少.先考慮一下樹形動歸.dp i 表示從1不能到以i為根的子樹中的所有查詢點的最小代價 考慮維護乙個量,mins i 表示從1到i路徑最...
虛樹學習筆記
虛樹常常被使用在樹形 dp 中。有些時候,我們需要計算的節點僅僅是一棵樹中的某幾個節點 這個時候如果對整棵樹都進行一次計算開銷太大了 所以我們需要把這些節點從原樹中抽象出來 按照它們在原樹中的關係重新建一棵樹,這樣的樹就是虛樹 在構建之前,我們需要把所有需要加入的節點按照 dfn 序從小到大排好序 ...
學習筆記 虛樹
模板 樹剖 lca 建虛樹 include using namespace std const int maxn 100000 10 int n,m,dp maxn vis maxn h maxn sta maxn top int fir maxn head maxn to maxn 1 nxt m...