虛樹常常被使用在樹形 \(dp\)中。
有些時候,我們需要計算的節點僅僅是一棵樹中的某幾個節點
這個時候如果對整棵樹都進行一次計算開銷太大了
所以我們需要把這些節點從原樹中抽象出來
按照它們在原樹中的關係重新建一棵樹,這樣的樹就是虛樹
在構建之前,我們需要把所有需要加入的節點按照 \(dfn\) 序從小到大排好序
在加點時,我們要用棧維護乙個最右鏈
在這個鏈左邊的虛樹都已經構建完成
我們設 \(top\) 為棧頂,設要加入的節點為 \(now\),設棧頂元素與 \(now\) 的 \(lca\) 為 \(lc\)
在加入的時候,會有以下幾種情況
此時我們直接把 \(now\) 接在最右鏈之後即可
\(2\)、\(lc\) 位於 \(sta[top]\) 和 \(sta[top-1]\)之間
此時 \(sta[tp]\) 已經不在最右鏈上,將其在虛樹上和 \(lc\) 連邊後出棧
同時把 \(lc\) 和 \(now\) 依次入棧
\(3\)、\(lc\) 為 \(sta[top-1]\)
和上面幾乎一樣,只是不把 \(lc\) 入棧
\(4\)、\(lc\) 的深度比 \(sta[top-1]\) 還小
我們把 \(sta[top]\) 和 \(sta[top-1]\) 連邊後出棧,重複之前的操作
這樣,我們直接在建出來的虛樹上 \(dp\) 就可以了
設總點數為 \(k\),則時間複雜度為 \(o(klogk)\)
以p2495 [sdoi2011]消耗戰為例
#include#include#include#include#define rg register
inline int read()
while(ch>='0' && ch<='9')
return x*fh;
}const int maxn=1e6+5;
int h[maxn],tot=1,h2[maxn],t2=1;
struct asdb[maxn],b2[maxn];
void ad(rg int aa,rg int bb,rg int cc)
void ad2(rg int aa,rg int bb)
int n,m,fa[maxn],dep[maxn],son[maxn],siz[maxn];
long long mindis[maxn];
void dfs1(rg int now,rg int lat)
}int dfn[maxn],dfnc,tp[maxn],stk[maxn],cnt,sta[maxn],js;
void dfs2(rg int now,rg int top)
}bool cmp(rg int aa,rg int bb) else
}break;
} else
} sta[++js]=now;
}bool vis[maxn];
long long dfs(rg int now,rg int lat)
if(vis[now]) else
vis[now]=0;
h2[now]=-1;
return cs;
}int main()
printf("%lld\n",dfs(1,0));
} return 0;
}
虛樹學習筆記
將關鍵點按dfs序排序後,所有關鍵點與相鄰關鍵點的lca合起來構成虛樹 通常還要加上整棵樹的根 虛樹至多有2k2k 個點。體現在實現中就是每次都pop若干點,並有機會push2個點。stk中存的是從根到當前點的遞迴棧中目前選入虛樹的點。stk中的點之間都未連邊 因為事實上關係還未確定 pop掉乙個點...
虛樹 學習筆記
水平不夠,學習來湊 又開了個天大的新坑 sdoi 2011 消耗戰 題目大意就是講 給出一棵樹,有邊權,然後給出k個查詢點,問從1號店不能到任何乙個查詢點的代價是多少.先考慮一下樹形動歸.dp i 表示從1不能到以i為根的子樹中的所有查詢點的最小代價 考慮維護乙個量,mins i 表示從1到i路徑最...
學習筆記 虛樹
模板 樹剖 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...