如何构建虚树

如何构建虚树?

当我们获得k个关键点后

首先把关键点按dfn(即原树的dfs序)从小到大排序

然后开一个栈

栈的意义(性质):从栈底到栈顶的元素构成(表示)虚树中从上到下的一条链

虚树构建过程:

依次枚举关键点x

  • 当栈为空或栈中只有一个元素(即top<=1,top从0开始),直接把x压入栈中(break/return)

  • 否则令lca=LCA(x,stk[top])

    • 如果lca=stk[top]

      ​ 说明x应该接在stk[top]的下面(在虚树中),所以直接把x压入栈中(break/return)

    • 如果lca!=stk[top]

      ​ 说明x和stk[top]分属lca的两棵不同的子树,而且stk[top]所在的子树中已经构建完成了,所以我们把lca的stk[top]所在的那棵子树弹栈,在弹栈的过程中建边(单向边),直到 dfn[stk[top]]<=dfn[lca]<=dfn[stk[top-1]] (即lca在栈顶的两元素的路径上) 或 栈中元素小于2的时候停止弹栈,并判断lca是否等于stk[top]

      • 若不等,先从lca向stk[top]连边,然后弹出栈顶,压入lca,再压入x

      • 否则直接压入x

枚举关键点结束后,若栈中的元素超过2个(即top>1),就不断从stk[top-1]向stk[top]连边,并弹出栈顶。

到此,虚树构建完成,可以愉快地DP了。

总之,这个过程看似复杂,但只要想着要始终维护栈的性质(从栈底到栈顶的元素构成虚树中从上到下的一条链)就不容易打错了。写的时候可以画个图,让自己思路清晰

上马:

void insert(int x){
    if(top==1){stk[++top]=x;return;}
    int lca=LCA(stk[top],x);
    if(lca==stk[top]){stk[++top]=x;return;}
    while(top>1&&dfn[stk[top-1]]>=dfn[lca]){
        ADD(stk[top-1],stk[top]); // 单向 
        --top;
    }
    if(lca!=stk[top]){
        ADD(lca,stk[top]);
        stk[top]=lca;
    }
    stk[++top]=x;
}
int main(){
    ...
    sort(x+1,x+k+1,cmp); //按dfn从小到大 
    top=0;ans=0;
    stk[++top]=1;
    for(int i=1;i<=k;++i){
        if(x[i]==1)continue;
        insert(x[i]);
    }
    while(top>1)ADD(stk[top-1],stk[top]),--top;
    ...
}

猜你喜欢

转载自www.cnblogs.com/Max-well/p/11823564.html