虚树
虚树
在处理一些树上的询问时,总询问点数为 $O(n)$, 但询问次数可能很多, 这时就需要虚树.
虚树的思想是尽可能用少的节点将询问点数串联起来.
对于询问点,虚树建立后只包含询问点之间的 $\mathrm{lca}$ 以及询问点本身.
虚树的构建
先对所有询问点按照 $\mathrm{dfs}$ 序排序.
维护一个栈,栈里的元素代表从上到下的一条链且栈顶为链底.(为了方便一般将根节点加入)
考虑现在加入询问点 $\mathrm{x}$.
分几种情况讨论:
1. 栈中元素个数小于等于 $1$ 则直接加入.
2. $\mathrm{LCA(x,s[top])=s[top]}$, 即 $\mathrm{x}$ 位于 $s[top]$ 的子树中,同样直接加入.
3. $\mathrm{LCA(x,s[top])!=s[top]}$, 即 $\mathrm{s[top]}$ 与 $\mathrm{x}$ 位于 $\mathrm{lca}$ 的两个子树中.
对于第 $3$ 种情况, 就不断弹栈, 并让栈顶元素变为 $\mathrm{lca}$, 再连 $\mathrm{lca}$ 与 $\mathrm{x}$ 的边.
注意:边弹栈边加入虚树边.
void ins(int x) {
if(top<=1) {
sta[++top]=x;
return ;
}
int lca=get_lca(sta[top], x);
if(lca == sta[top]) {
// x 在 sta[top] 子树中.
sta[++top] = x;
}
else {
while(top>1&&dfn[sta[top-1]]>=dfn[lca]) G[sta[top-1]].pb(sta[top]),--top;
// top, lca, top-1
if(lca!=sta[top]) G[lca].pb(sta[top]),sta[top]=lca;
sta[++top]=x;
}
}
例题
洛谷P2495 [SDOI2011]消耗战
虚树裸题, 题目数据范围是假的, 边权要开 long long.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 250009
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const ll inf=10000000000000ll;
int n ;
ll mn[N],val[N<<1];
int sta[N],top;
vector<int>G[N];
int hd[N],to[N<<1],nex[N<<1];
int dep[N],fa[20][N],a[N],dfn[N],mk[N],edges,scc;
void add(int u,int v,ll c) {
nex[++edges]=hd[u];
hd[u]=edges;
to[edges]=v;
val[edges]=c;
}
void dfs(int x, int ff) {
fa[0][x]=ff;
dfn[x]=++scc;
for(int i=1;i<=19;++i) fa[i][x]=fa[i-1][fa[i-1][x]];
for(int i=hd[x];i;i=nex[i]) {
int v=to[i];
if(v==ff) continue;
dep[v]=dep[x]+1;
mn[v]=min(mn[x], 1ll*val[i]);
dfs(v, x);
}
}
int get_lca(int x, int y) {
if(dep[x] > dep[y]) swap(x, y);
if(dep[x] != dep[y]) {
for(int i=19;i>=0;--i)
if(dep[fa[i][y]] >= dep[x]) y=fa[i][y];
}
if(x==y) return x;
for(int i=19;i>=0;--i)
if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
bool cmp(int i,int j) { return dfn[i] < dfn[j]; }
void ins(int x) {
if(top<=1) {
sta[++top]=x;
return ;
}
int lca=get_lca(sta[top], x);
if(lca == sta[top]) {
// x 在 sta[top] 子树中.
sta[++top] = x;
}
else {
while(top>1&&dfn[sta[top-1]]>=dfn[lca]) G[sta[top-1]].pb(sta[top]),--top;
// top, lca, top-1
if(lca!=sta[top]) G[lca].pb(sta[top]),sta[top]=lca;
sta[++top]=x;
}
}
ll calc(ll x) {
ll re=mn[x],sum=0;
for(int i=0;i<G[x].size();++i)
sum+=calc(G[x][i]);
if(mk[x]) re=mn[x];
else re=min(sum, 1ll*mn[x]);
return re;
}
void clr(int x) {
mk[x]=0;
for(int i=0;i<G[x].size();++i) clr(G[x][i]);
G[x].clear();
}
void solve() {
int k;
scanf("%d",&k);
for(int i=1;i<=k;++i) scanf("%d",&a[i]);
sort(a+1, a+1+k, cmp);
// 去重.
k=unique(a+1, a+1+k)-a-1;
sta[top=1]=1;
for(int i=1;i<=k;++i) mk[a[i]]=1,ins(a[i]);
while(top>1) G[sta[top-1]].pb(sta[top]),--top;
printf("%lld\n",calc(1));
clr(1),top=0;
}
int main() {
// setIO("input");
scanf("%d",&n);
for(int i=1;i<n;++i) {
int x,y;
ll z;
scanf("%d%d%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
mn[1]=inf;
dfs(1, 0);
int m;
scanf("%d",&m);
for(int i=1;i<=m;++i) solve();
return 0;
}

浙公网安备 33010602011771号