\(Warning:\) 全程无图,只是写得能让自己看懂而已,不推荐用此篇入门
何为虚树
把原树的\(k\)个节点提出来建一棵新树,使这棵新树的欧拉序 = 把原树那\(k\)个节点的入/出栈序单独提出来,再sort一遍的顺序,这棵新树就是原树那\(k\)个节点对应的虚树
构建方法
本着先进先出的原则其实只要记录\(dfn\)就可以保证一条链的欧拉序
所以把要构建虚树的点按照\(dfn\)排序,然后用栈维护一条链
假设我们要新加入点\(p\),栈顶为\(x\),栈顶的下一个元素为\(y\)
因为已经按照\(dfn\)排序,所以不会出现\(lca(x,y)==y\)或\(lca(x,p)==p\)的情况
如果\(、p、x\)在同一条链,直接把\(p\)入栈
如果\(lca(p,x)!=x\),分情况讨论:
1.\(dfn[y]>dfn[lca(p,x)]\)
连\(y \rightarrow x\)的边,然后将\(x\)出栈
2.\(dfn[y]==dfn[lca(p,x)]\)
即\(y==lca(p,x)\),连\(y \rightarrow x\)的边,然后将\(x\)出栈
3.\(dfn[y]<dfn[lca(p,x)]\)
连\(lca(p,x)\rightarrow x\)的边,保证欧拉序要先把\(x\)出栈,再将\(lca(p,x)\)入栈(相当 于给这条链换了个方向?)
当然最后还是要把\(p\)入栈的
代码:
void insert(int p){
int ff=lca(p,st[fr]);
if(fr==1 || ff==st[fr]){ st[++fr]=p;return; }
while(fr>1 && dfn[st[fr-1]]>=dfn[ff]) add(st[fr-1],st[fr]),fr--;
if(ff!=st[fr]) add(ff,st[fr]),st[fr]=ff;
st[++fr]=p;
}
void build(int num){
ll tmp=0;
sort(d+1,d+num+1,cmp);//按dfn从小到大排序
fr=0,st[++fr]=0;
for(int i=1;i<=num;++i) insert(d[i]);
}
应用
优化树形dp
咕咕咕咕咕