虚树
-
适用范围
- 虚树常常用来处理询问的点数远小于处理询问要经过的树上的点数的一类问题。
-
简单做法
- 把所有点和他们任意两个的LCA求出来,将它们按照父子关系构建一棵树,
- 还要想好两两之间的边权如何处理
-
构建方法
- 首先把所有询问的点按照在原树的 d f s dfs dfs序排序,我们还需要一个栈来维护从根到当前节点的链。
- 把 1 1 1号点加进去,为了保证3不会把栈弹空
- 每枚举到一个点,如果栈为空,直接入栈,否则求它和栈顶的
l
c
a
lca
lca。 对于这个
l
c
a
lca
lca和栈顶元素的
d
f
s
dfs
dfs序有下面三种关系:
- d f n [ l c a ] = = d f n [ s [ t o p ] ] dfn[lca]==dfn[s[top]] dfn[lca]==dfn[s[top]],说明栈顶就是 l c a lca lca,直接把当前元素入栈。
- d f n [ l c a ] < d f n [ s [ t o p ] ] 但 是 d f n [ l c a ] > = d f n [ s [ t o p − 1 ] ] dfn[lca]<dfn[s[top]] 但是 dfn[lca]>=dfn[s[top-1]] dfn[lca]<dfn[s[top]]但是dfn[lca]>=dfn[s[top−1]],由于我们维护的是一个链,就可以让栈顶出栈, l c a lca lca入栈
- 否则,一直 p o p pop pop到出现上面的情况
- 加完 l c a lca lca加最后再加枚举到的点。
- 至于连边,显然一个点出去的时候,它只会和上面的那个点连边,所以弹出的时候要连下边,最后如果栈不为空也要连边。
- 在实践中其实不需要判断 d f s dfs dfs序,只需要判断深度就可以辣。
-
参考代码
-
sort(a+1,a+1+m,cmp); if(!bel[1]) st[++top]=1; int temp; for(int i=1;i<=m;i++) { if(!top) { st[++top]=a[i]; continue; } temp=lca(a[i],st[top]); while(1) { if(dep[st[top-1]]<=dep[temp]) { if(temp!=st[top]) add(temp,st[top]); top--; if(st[top]!=temp) st[++top]=temp; break; } add(st[top-1],st[top]); top--; } if(st[top]!=a[i]) st[++top]=a[i]; } while(top>1) add(st[top-1],st[top]),top--;
-
-
经典例题
- 2020牛客暑期多校训练营(第一场)
- 这个题有一个虚树上的重要结论:每次求 l c a lca lca本质上是求和上一个要添加的元素的 l c a lca lca,即不会出现求 l c a lca lca和 l c a lca lca的 l c a lca lca
- 2020牛客暑期多校训练营(第一场)