树链剖分小结

这两周在学树剖。

先扔个模板

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

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

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

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

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

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

   

树链剖分(节选自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天天爱跑步

原文地址:https://www.cnblogs.com/y-m-y/p/6677723.html