日常学习——Virtual_Tree(虚树)

概念

虚树,顾名思义就是虚构的树,它是一种用来解决树上问题的算法,主要思想是只将原树上必要的点和它们的最近公共祖先取出来,构成一棵虚树,并保留他们在树上的相对关系。

引入

我们先来看一道题:
给定一棵n个点的树,每次询问给定一个大小为k的点集,你需要切掉一些边,使得点集中的点均不与1号点联通,而每条边都有被切掉所需的代价,你还要让总代价最小。\(2 \leqslant n \leqslant 250000,,\sum ki \leqslant 500000,1 \leqslant k_i \leqslant n-1\)
(来源:Bzoj_2286 Sdoi2011消耗战)
对于单个询问,我们可以用Tree_Dp在O(n)的时间内得到答案,这样显然太慢。但是,我们发现实际上询问的总点集大小并不大,如果每次询问能不将整棵树都遍历而只遍历询问的点,那么复杂度就可以降下来了。因此我们需要用到虚树。

构建

构建虚树采取的思路是:将点“从左到右”地添加。
我们先要将树遍历一遍给每个点记一个dfs序,然后将询问点按dfs序排序。
用栈维护一条当前虚树内“最靠右”的一条链,按顺序加点。
假设当前准备加入的点为x,如果当前栈顶不是x的祖先,则弹栈,并向上一个弹出的点(仅在本轮操作中)连边,直到是或栈为空为止,顺便记录弹出的点与点x的最近公共祖先LCA(弹出的所有点与点x的最近公共祖先一定相同)。
将LCA与最后一个弹栈的点连边,之后再判断LCA是否已在栈顶,否则入栈。点x入栈。这样栈中一定是“最靠右”的链。
复杂度\(O(klogn)\)
以下图为例,绿色点表示询问点,蓝色点表示进入虚树的点,红色点表示栈里的点。
1331002-20190212212322199-1661475665.png
加入2号点:
1331002-20190212212437529-2124027464.png
加入4号点:
1331002-20190212212550199-715821770.png
加入6号点,2和4连边:
1331002-20190212212731830-310503837.png
加入9号点,2和6连边、1和2连边:
1331002-20190212212900075-1940295610.png
最后再将栈内的1和9连边。

解决

那么我们回到原题,现在知道如何建虚树了,如果解决问题?
首先我们要有一个小技巧,就是对于一个询问点,如果某个询问点是它的祖先,那么就可以把它从询问点中删除。这样询问点之间就没有祖先关系了。我们还需要为每个点记一个值,为它到根(1号点)的路径中最小代价的边,记为\(v_x\)
那么建出虚树后,我们对于虚树中的叶子节点x,其dp值为\(v_x\)。对于非叶子节点x,dp值为\(min(v_x,\sum dp_{son})​\),son为它的直系儿子,1号点的dp值即为最终答案。

后续

提供一个建虚树的板子
1331002-20190213100824920-1033756352.png

这里还有些用到虚树的题,可供参考。
Bzoj 3991 SDOI2015寻宝游戏
Bzoj 3611 Heoi2014大工程
Bzoj 3572 Hnoi2014世界树
Luogu_4242 树上的毒瘤
Bzoj 5287 Hnoi2018毒瘤

转载于:https://www.cnblogs.com/Alseo_Roplyer/p/10367231.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值