zoukankan      html  css  js  c++  java
  • 日常学习——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))
    以下图为例,绿色点表示询问点,蓝色点表示进入虚树的点,红色点表示栈里的点。

    加入2号点:

    加入4号点:

    加入6号点,2和4连边:

    加入9号点,2和6连边、1和2连边:

    最后再将栈内的1和9连边。

    解决

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

    后续

    提供一个建虚树的板子

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

  • 相关阅读:
    导航控制器的出栈
    UIPickView的基本使用
    多控制器
    通过Xib加载控制器的View
    从StoryBoard加载控制器
    模仿UIApplication单例
    LaunchScreen原理
    UIWindow
    指定初始化的运用
    零长度数组在内核中的运用
  • 原文地址:https://www.cnblogs.com/Alseo_Roplyer/p/10367231.html
Copyright © 2011-2022 走看看