有一类问题,整棵树的节点数非常多,但是询问只和其中的若干个关键节点(以及必要的维护他们之间的连通性的节点)有关,这类问题我们可以采用虚树来优化。
一般采用单调栈构建虚树,首先将所有关键节点按 \(dfn\) 排序,把 \(1\) 号节点入栈。
单调栈中维护的一直是一条链,我们扫一遍所有的节点,判断这个节点和栈顶的 LCA 是不是栈顶,如果不是就进入判断阶段,否则说明二者在一条链上。
判断次大节点的 dfn 是否比 LCA 大,如果是,说明次大节点的深度大(栈顶更大),弹出栈顶并与次大节点连边。
判断一下 LCA 是否等于当前的次大节点,如果不是则弹出栈顶并连与 LCA 连边,把 LCA 入栈。
如果不是说明 LCA 已经是次大节点了,直接弹出栈顶并与 LCA 连边即可。
最后把当前节点入栈,继续遍历。
code:
点击查看代码
void build(int n){
sort(imp + 1, imp + n + 1, cmp);
memset(head, 0, sizeof(head));
cnt = 0;
s[top = 1] = 1;
for(int i = 1; i <= n; i++){
if(imp[i] == 1) continue;
int lc = lca(s[top], imp[i]);
if(lc != s[top]){
while(dfn[s[top-1]] > dfn[lc])
add(s[top], s[top-1]), top--;
if(lc != s[top-1])
add(s[top], lc), s[top] = lc;
else add(lc, s[top]), top--;
}
s[++top] = imp[i];
}
for(int i = 1; i < top; i++)
add(s[i], s[i+1]);
}