虚树是一种神奇的算法,它的具体思想为在一棵较为庞大的树当中,我们可能并不需要考虑该树上的所有节点,而是只需要考虑其中的一部分的话,我们可以使用这些点组建一棵虚树。虚树仍可以维护这些点之间的祖孙,兄弟等关系。一般来说,多次询问且\(\Sigma\)点数复杂度可接受为虚树的提示。
如何组建一棵虚树?
首先我们先抛出一个结论,将这些需要考虑的节点按dfs序排序,虚树需要的点仅为这些点及两两相邻的节点的lca。
证明略
好了,我们现在已经证毕这个结论了,具体的建树方法如下:
1.首先,我们需要维护一个栈,这个栈无论何时都是一条链(栈顶为最深的节点),我们先把根节点入栈
2.我们一次考虑已经按照dfs序排列好的点,对于一个新点,我们找到他与栈顶的lca
3.我们首先将栈顶依次弹出,并每次将弹出的点与新栈顶连边,直到栈顶深度比lca的小
4.我们把lca和新点一次入栈(注:若有重复点则不在入栈)
5.当我们考虑完所有点之后,我们把栈中的所有点依次连边
点击查看代码
scanf("%d",&k);
for(int i=1;i<=k;++i) scanf("%d",&h[i]),point[h[i]]=1;
sort(h+1,h+k+1,[](int a,int b){return dfn[a]<dfn[b];});
st[tp=1]=1;
for(int i=1;i<=k;++i){
int L=lca(st[tp],h[i]);
while(dep[st[tp-1]]>dep[L]){
E[st[tp-1]].emplace_back(st[tp]);
E[st[tp]].emplace_back(st[tp-1]);
tp--;
}
if(L==st[tp]){st[++tp]=h[i];continue;}
E[st[tp]].emplace_back(L);
E[L].emplace_back(st[tp]);
--tp;
if(L!=st[tp]) st[++tp]=L;
st[++tp]=h[i];
}
for(int i=1;i<tp;++i)
E[st[i]].emplace_back(st[i+1]),E[st[i+1]].emplace_back(st[i]);