虚树

给定树上一些点集,处理和该点集有关的询问,通常这些询问需要用树形\(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;
}
posted @ 2020-01-22 20:58  lhm_liu  阅读(172)  评论(0编辑  收藏  举报