• 虚树


    对于一株树,如果我们需要在一株n个结点组成的树上做树上DP,但是其中仅有m个结点需要特殊处理。显然我们可以在原树上做一次完整的树上DP,其时间复杂度为O(n)。但是有的时候m远小于n,且这种请求可能不止发生一次,那么我们将无法在给定时间内完成任务。

    实际上每次DP真正涉及到的结点数并不多,其仅为m个结点以及m个结点中任意两个结点的lca。所以如果我们能借助这些m个结点和结点对所指出的lca按照树中的祖先后代关系重组一株新树,树的大小将大幅缩小,这样就能完成任务了,而新建立的树称为虚树。

    说一下流程:

    1.首先我们对树进行先序遍历,并按照结点访问的顺序记录结点属性dfn,dfn越小表示其在先序遍历时越早被访问。这部分时间复杂度为O(n)。

    2.之后我们利用st或树上倍增算法对树进行预处理,这部分时间复杂度为O(nlog2n)。

    3.之后我们处理所有的请求,对每个请求建立虚树,并在虚树上做DP操作。

    建立虚树的流程如下:

    1.选取所有特殊结点,并加入到列表中。

    2.对列表按照dfn属性进行从小到大排序。

    3.将每个列表中的特殊结点与与其左右相邻的特殊结点之间的lca加入到列表中。

    4.重新按照dfn属性排序列表。

    5.建立一个栈,将列表中dfn属性最小(下标最小)的结点加入到栈中。

    6.之后从前到后遍历列表中的剩余元素。弹出栈尾所有非当前遍历元素祖先的结点,并将当前遍历元素的虚树父亲设为栈尾元素,之后将当前遍历元素加入到栈尾。重复这个过程。

    先说明建立虚树的步骤3会将任意两个特殊结点的lca加入到虚树中。考虑a,b,c为三个前后相邻的特殊结点。很显然lca(a,b)与lca(b,c)有祖先后代关系,因为二者均为b的祖先,且lca(a,c)=lca(lca(a,b),lca(b,c))。故利用传递性我们能保证任意两个特殊结点的lca都被加入到了列表中。

    而第5步,我们建立栈并将dfn属性最小的结点加入到栈中,若栈尾元素不是当前遍历元素的祖先结点,而当前遍历元素一定处于栈中,则表示栈尾元素所代表的子树已经建立完毕,我们将其弹出则表示不会有新的结点追加到其后。(这都是dfn的性质)

    很显然对于m个特殊结点,我们仅仅选出了拥有2m-1个结点,即我们建立的树相对于问题的规模是线性的。

  • 相关阅读:
    P1074 靶形数独
    PYTHON-模块 sys os random shutil-练习
    PYTHON-模块-time&datetime-练习 +目录规范
    PYTHON-模块time&datetime+ 目录规范
    PYTHON-模块定义 搜索路径
    PYTHON-匿名函数,递归与二分法,面向过程编程-练习
    PYTHON-匿名函数,递归与二分法,面向过程编程
    PYTHON-有参装饰器,无参装饰器,语法糖
    PYTHON-迭代器,xxx生成式
    PYTHON-函数对象,嵌套,名称空间与作用域,闭包函数
  • 原文地址:https://www.cnblogs.com/dalt/p/8982599.html
Copyright © 2020-2023  润新知