虚树用来解决询问多个关键点,且关键点总数不多的一类问题,记每次询问的关键点个数为n,通常n1e5是用到虚树的题目的共性。
当树上部分节点用处不大的时候,我们可以把它们从树上删掉,只保留关键点和它们的LCA
虚树的点数级别是Θ(n)的,因为只包含了n个关键点以及它们的LCA,所以总点数最多是2n1个。
构造虚树:

  • 首先我们需要把LCA预处理出来,可以用ST表或者树链剖分,顺便处理出树的dfs序。
  • 将询问的关键点按照dfs序排序,为了方便我们先把根加入数组中,求出相邻两点的LCA,加入关键点数组中重新再按照dfs序排序,并去重,现在我们已经得到了虚树上所有的点的前序遍历。
  • 如果当前点在栈顶子树中 将栈顶和当前点连边,并将当前点入栈。
  • 如果当前点不在栈顶子树中,说明此时栈顶所在的一段链已经Dfs完毕,弹栈,重复执行该过程直到满足情况1。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
inl void build() {
memset(head, 0, sizeof(head)), tot = 0;
sort(a.begin(), a.end(), [](int a, int b) -> bool {return dfn[a] < dfn[b]; });
int tmp = a.size();
for (int i = 0; i < tmp - 1; i++)
a.push_back(lca(a[i], a[i + 1]));
sort(a.begin(), a.end(), [](int a, int b) -> bool {return dfn[a] < dfn[b]; });
auto it = unique(a.begin(), a.end());
a.erase(it, a.end());
rt = st[++top] = a[0];
for (int i = 1; i < a.size(); i++) {
int now = a[i];
while (top >= 1) {
if (fir[now] >= fir[st[top]] && fir[now] <= las[st[top]]) {
add(st[top], now);
break;
}
top--;
}
if (st[top] != now) st[++top] = now;
}
}