zoukankan      html  css  js  c++  java
  • 树链剖分小结

    这两周在学树剖。

    先扔个模板

    有一类题目,要求实现一类在树上的操作,比如:

      修改/求 树上某 节点/边权 的(最)值;

      修改/求 树上某 节点/边权 及其子树上所有节点的(最)值;

      修改/求 树上某两点路径间的 节点/边权 的(最)值;

    乍一看似乎用线段树就可以实现,但是如果仔细想想,可以发现单凭线段树是无法解决的。

     对于这类题目,常用的解决方法是树链剖分。

       

    树链剖分(节选自starszys博客):

    相关定义:

      重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
      轻儿子:v的其它子节点。
      重边:点v与其重儿子的连边。
      轻边:点v与其轻儿子的连边。
      重链:由重边连成的路径。
      轻链:轻边。

      树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。
      记siz[v]表示以v为根的子树的节点数

      d[v]表示v的深度(根深度为1)

      top[v]表示v所在的重链的顶端节点

      f[v]表示v的父亲

      son[v]表示与v在同一重链上的v的儿子节点(重儿子)

      id[v]表示:

        1.v与其父亲节点的连边(v的父边)在线段树中的位置;

        2.v在线段树中的位置;

      只要把这些东西求出来,就能用logn的时间完成上述问题中的操作。

       

      实际上,只需要两次dfs就可以求出上述变量。

      dfs1(dfs):求出f、d、siz、son

      dfs2(build):求出id与top(详见模板)

      然后将原树中各点的值update到线段树中,进行各种操作。

    基本操作:

      1.修改/求 树上某 节点/边权 的值:根据id简单query()即可;

      2.修改/求 树上某两点路径间的 节点/边权 的(最)值:顺着top[],将top[]深度较大的节点向上调整,调整过程中进行区间查询即可;

      3.修改/求 树上某 节点/边权 及其子树上所有节点的(最)值:一般来说是用DFS序+线段树,但是经过树链剖分,任意一棵子树内的节点的id必然是连续的,区间修改[id[v],id[v]+siz[v]-1]即可;

       

    时间复杂度:

    既然树剖这么好用,那它的时间复杂度如何呢?

    可以证明,树链剖分执行一次修改/查询的时间复杂度是O(logn·logn)。

    经过上面的学习,可以发现树链剖分的结构是沿着链向上跳+线段树修改/查询。

    线段树修改查询操作的时间复杂度是O(logn),而在树链上每走一条轻边,子树大小就/=2,所以最多走log n条轻边。

    一个还不错的博客

    推荐题目:

      模板题:BZOJ4034、BZOJ1036(LZOJ21004)、BZOJ2243、POJ3237

      稍难:BZOJ3531、BZOJ3626、NOIp2015day1T3、NOIp2016天天爱跑步

  • 相关阅读:
    apk反编译
    mysql 安装解读
    安卓=--确认
    安卓--界面--改变image view
    安卓--返回时,不丢失转态
    安卓--跳转
    安卓--菜单
    安卓--Toast
    设置网页上收藏夹的图标
    分帧标签
  • 原文地址:https://www.cnblogs.com/y-m-y/p/6677723.html
Copyright © 2011-2022 走看看