題面傳送門
虛樹,顧名思義是虛的,就是不能完全代表一棵樹的樹。
換句話說,虛樹上只保留對我們有用的節點。而其它節點就沒有必要存在。
可以來看一道例題[sdoi2011]消耗戰
我們很容易發現,如果只保留樹上的資源點和資源點兩兩之間的lca,那麼其餘的點全部是沒有用的。
同時我們又知道結論:\(n\)個點兩兩的\(lca\)只有最多\(n-1\)個點。
所以這樣子建出來的虛樹一定是可以的。
說的好聽,這怎麼建啊
我們先將所有資源點按dfs序公升序排序,然後依次插入樹中。
具體的,維護乙個dfs序公升序的棧,這個棧表示虛樹最右邊的一條鏈。因為我插入的點dfs序遞增,我只需要知道最右邊的資訊即可。
然後設棧頂為\(top\),棧頂的下面乙個是\(y\),待插入的點為\(x\),\(lcas=lca(top,x)\)那麼有分類討論:
如果\(d[lca],即我不接著這條鏈走,我要換一條鏈走,所以不斷彈出棧與連邊直至這個條件不滿足。
然後這時又有情況:
如果此時的\(lca=top\),那麼什麼都不用做。
如果不等於,那麼\(d_top\)一定小於\(d_lca\),而這時又是換一邊走,所以把連邊然後\(top\)彈出然後看看\(lcas\)是否為\(y\)然後看是否加入即可。
最後把\(x\)加入,別忘了最後把棧中的邊加入虛樹。
最後樹形\(dp\)即可。
時間複雜度\(o(mlogn+\sum\limitslogn)\)
code:
#include#define ll long long
#include#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,st[250039],now,lcas,pus,sh,dh,flag[250039],a[250039],d[250039],top[250039],siz[250039],son[250039],fa[250039],dfn[250039];
ll w[250039],dp[250039];
struct yyy;
struct ljb;h[x]=head;}
}s,g;
inline void dfs1(int x,int last)
inline void dfs2(int x,int last)
inline int lca(int x,int y)
inline bool cmp(int x,int y)
inline void dfs(int x)
int main()
while(--sh)g.add(st[sh],st[sh+1],0);dfs(1);
printf("%lld\n",dp[1]); for(j=1;j<=k;j++) flag[a[j]]=0;
}}
虛樹學習筆記
將關鍵點按dfs序排序後,所有關鍵點與相鄰關鍵點的lca合起來構成虛樹 通常還要加上整棵樹的根 虛樹至多有2k2k 個點。體現在實現中就是每次都pop若干點,並有機會push2個點。stk中存的是從根到當前點的遞迴棧中目前選入虛樹的點。stk中的點之間都未連邊 因為事實上關係還未確定 pop掉乙個點...
虛樹 學習筆記
水平不夠,學習來湊 又開了個天大的新坑 sdoi 2011 消耗戰 題目大意就是講 給出一棵樹,有邊權,然後給出k個查詢點,問從1號店不能到任何乙個查詢點的代價是多少.先考慮一下樹形動歸.dp i 表示從1不能到以i為根的子樹中的所有查詢點的最小代價 考慮維護乙個量,mins i 表示從1到i路徑最...
虛樹學習筆記
虛樹常常被使用在樹形 dp 中。有些時候,我們需要計算的節點僅僅是一棵樹中的某幾個節點 這個時候如果對整棵樹都進行一次計算開銷太大了 所以我們需要把這些節點從原樹中抽象出來 按照它們在原樹中的關係重新建一棵樹,這樣的樹就是虛樹 在構建之前,我們需要把所有需要加入的節點按照 dfn 序從小到大排好序 ...