虚树
给定树上一些点集,处理和该点集有关的询问,通常这些询问需要用树形\(DP\)解决
若发现所有询问的点集总大小\(\sum k\leqslant 10^5\),那么就可以考虑用虚树来解决
若原树为
若询问点为\(1\ 2\ 3\),则虚树为
若询问点为\(1\ 3\ 7\ 8\),则虚树为
构造出的虚树为询问点和询问点的\(LCA\),其他不重要的点和边都进行了类似于路径压缩的操作
虚树的构建为增量算法,先将每个询问点按\(dfs\)序排序,用栈来实现,栈内为从根到栈顶元素这条链,这条链同时也是虚树上的链
实现时,先将根节点入栈
当要向虚树内插入节点\(x\)时,分若干情况讨论
① 若栈内只有根节点(\(top=1\)),则直接将\(x\)入栈
② 若\(lca(x,st[top])=st[top]\),说明\(x\)和\(st[top]\)在同一条链上,则
③ 若\(lca(x,st[top])\ne st[top]\),则说明\(x\)和\(st[top]\)分别在两棵不同的子树中,这时\(st[top]\)所在的子树已经在虚树上构建完。那么开始退栈,直到将\(st[top]\)所在的子树全部退出栈,同时连虚树上的边,若操作完后\(lca\)不在栈中,再将其入栈以及连边,最后将\(x\)入栈
最后还需将栈清空,将栈内剩余的点进行连边,构建完整个虚树
\(code:\)
bool cmp(const int &a,const int &b)
{
return dfn[a]<dfn[b];
}
void insert(int x)
{
if(x==1) return;
if(top==1)
{
st[++top]=x;
return;
}
int anc=lca(x,st[top]);
if(anc==st[top])
{
st[++top]=x;
return;
}
while(top>1&&dfn[anc]<=dfn[st[top-1]]) add(st[top-1],st[top]),top--;
if(anc!=st[top]) add(anc,st[top]),st[top]=anc;
st[++top]=x;
}
......
sort(query+1,query+k+1,cmp);
st[top=1]=1;
for(int i=1;i<=k;++i) insert(query[i]);
while(top) add(st[top-1],st[top]),top--;
注意在树形\(DP\)后要清空\(head\),若每次建虚树时\(memset\),那么就无法保证时间复杂度
void dp(int x)
{
......
head[x]=0;
}