[算法学习] 虚树

概念

虚树,是对于一棵给定节点数\(n\)的树\(T\),构造一棵新的树\(T′\)使得节点总数最小且包含指定的某几个节点和它们的\(\text{LCA}\)

利用虚树,可以对于指定多组点集\(S\)的询问进行每组\(\Theta(|S|\log n+f(|S|))\)的回答,其中\(f(x)\)指的是对于树上x个点的情况下单组询问这个问题的时间复杂度.可以看到,这个复杂度基本上与n无关了.

这样,对于多组询问的回答就可以省去每次询问都遍历一整棵树的\(\Theta(n)\)复杂度了.

虚树的构建

首先最暴力的方法,就是枚举点集上的点,\(\Theta(|S|^2 \log n)\)的枚举集合上的点,求\(\text{LCA}\),然后加入集合之中。

那么我们考虑如何\(\Theta(|S| \log n)\)的去构建这棵树。

我们先对\(S\)集合中的点按照\(\text{dfn}\)排序。

我们维护一个栈,自栈底到栈顶的元素的\(\text{dfn}\)单调递增,即栈底到栈顶的元素表示根节点到\(u\)的一条路径。

现在分情况讨论

如果\(\text{stack}\)中的元素只有一个(只有根节点) 这种情况最简单,直接将这个节点加进去。

否则 继续分情况考虑 下文中默认栈顶元素为\(stk[top]\) 新加入的点为\(x\) \(LCA\)\(lca\)

我们求\(lca = LCA(stk[top], x)\)(原树中)。

显然\(lca\)不可能为\(x\),因为若成立\(dfn[lca = x] <= dfn[x], dfn[stk[top]]\) 但是\(dfn[x] > dfn[lca]\) (加入的顺序) 矛盾。

所以\(lca\)要么等于\(stk[top]\),那么这种情况很简单,直接将\(x\)放入栈中。

否则,说明\(x\)\(stk[top]\)不属于同一棵子树,同时也说明\(stk\)中的元素已经构建完毕了,

所以我们要不断弹出元素,在弹出元素的同时不断的连边,知道\(dfn[stk[top - 1]] < dfn[lca]\)或者\(top < 2\)为止.

然后若此时的\(stk[top]\)\(lca\)相同,那么我们直接加这个元素,否则我们把\(lca\)\(stk[top]\)连接起来,然后在将\(stk[top]\)弹出,\(lca\)\(x\)压入。

汇总一下 代码如下

inline void insert(int x) {
      if(top == 1) {stk[++top] = x; return;}
      int lca = LCA(x, stk[top]);
      if(lca == stk[top]) {stk[++top] = x; return;}
      while(top > 1 && dfn[stk[top - 1]] >= dfn[lca]) {
            Addedge(stk[top - 1], stk[top]);
            top -- ;
      }
      if(stk[top] != lca) {
            Addedge(lca, stk[top]);
            stk[top] = lca;
      }
      stk[++top] = x;
}
posted @ 2020-08-24 21:29  Hock  阅读(219)  评论(0编辑  收藏  举报