给定树上一些点集,处理和该点集有关的询问,通常这些询问需要用树形(DP)解决
若发现所有询问的点集总大小(sum kleqslant 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]) e 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;
}