• 树的经典问题和方法


    树的经典问题和方法

    《算法竞赛入门经典(第2版)》392页

    欧拉序列。对有根树t进行dfs(深度优先遍历),无论是递归还是回溯,每次到达一个结点时都将深度记录下来,可以得到一个长度为2n-1的序列,称为t的欧拉序列f(类似于欧拉回路)。

    为了方便,把结点k在欧拉序列中第一次出现的序号记为pos(k)。

    有了欧拉序列,lca问题可以在线性时间内化为rmq问题:lca(t,u,v)=rmq(b,pos(u),pos(v))。这里rmq返回的是下标而不是值本身。

    这个等式不难理解:从u走到v的过程中一定会经过lca(t,u,v),但不会经过lca(t,u,v)的祖先。因此,从u走到v的过程中经过的深度最小的结点就是lca(t,u,v)。

    用dfs计算欧拉序列的时间复杂度是o(n),且欧拉序列的长度是2n-1=o(n),所以lca问题可以在o(n)的时间内转化成等规模的rmq问题。

    树的动态查询问题1。给定一棵带边权的树,要求支持两种操作:修改某条边的权值和询问树中两点间的距离。

    首先把无根树变成有根树,则把一条边u-v(假定u是v的父亲结点)的权值增加d时,以u为根的整个子树的“到根节点的距离”同时增加d。不难发现,一棵子树内的结点对应欧拉序列中的一段连续序列,因此如果用dist[i]表示欧拉序列中第i个结点到根的距离,则修改操作就是dist数组上的“区间增量”,而查询时的距离(u,v)等于dist(u)+dist(v)-2dist(w),其中w=lca(u,v)。这样,只需用一个支持快速区间增量和单点查询的数据结构(例如fenwick树或者线段树)来维护dist数组,就可以在o(log n)的时间内支持两个操作。

    轻重路径剖分。给定一棵有根树,对于每个非叶节点u,设u的子树中结点数最多的子树的树根为v,则标记(u,v)为重边,从u出发往下的其它边均为轻边。

    根据上面的定义,只需一次dfs就能把一棵树分解成若干重路径(重边组成的路径)和若干轻边。有些资料也把重路径称为树链,因此轻重路径剖分也称树链剖分。

    路径剖分最重要的定理如下:若v是u的子结点,(u,v)是轻边,则size(v)<size(u)/2,其中size(u)表示以u为根的子树中的结点总数。

    证明并不复杂。由定义,所有非叶结点都有一条重边。假设size(v)>=size(u)/2,那么对于u向下的重边(u,w)来说,size(w)>=size(v)>=size(u)/2,因此size(u)>=1+size(v)+size(w)>=1+size(u),与假设矛盾。

    由此可以得到如下重要结论:对于任意非根结点u,在u到根的路径上,轻边和重路径的条数均不超过log2n因为每碰到一条轻边size值就会减半。

    树的动态查询问题2。给定一棵带边权的树,要求支持两种操作:修改某条边的权值和询问树中两点之间唯一路径上的最大边权。

    首先把无根树变成有根树并且求出路径剖分。任意结点u到其根节点x的简单路径中包含一些轻边和重路径,但这些重路径可能并不是原树中的完整重路径,而是一些“片段”,因此可以在轻边中直接保存边权,而用线段树维护重路径。这样两个操作都不难实现。

    修改:轻边直接修改,重边需要在重路径对应的线段树中修改。

    查询:设lca(u,v)=p,则只需求出u到其祖先p之间的最大变权maxw(u,p),再用类似的方法求出maxw(v,p),则答案为max{maxw(u,p),maxw(v,p)}。为了求出maxw(u,p),依次访问u到p之间的每条重路径和轻边即可。根据刚才的结论,轻边和重路径的条数均不超过log2n。这样,修改的时间复杂度为o(log n),查询的时间复杂度为o(log2n)。虽然存在时间复杂度更低的方法,但上述方法已经很实用了。

  • 相关阅读:
    C语言使用指针表示数组的注意事项
    LUNA16数据集的百度云链接
    c语言定义指针类型需注意事项
    对DeepLung数据预处理部分的详细展示
    python统计字符串中字符个数
    C语言预处理命令之文件包含
    C语言值拷贝传递机制
    【论文】CornerNet:几点疑问
    解读 pytorch对resnet的官方实现
    python 对三维CT数据缩放
  • 原文地址:https://www.cnblogs.com/JebediahKerman/p/5990240.html
Copyright © 2020-2023  润新知