zoukankan      html  css  js  c++  java
  • 树链剖分 笔记

    前言

    树链剖分用于转化树上的问题,使得它更容易考虑,解决。 主要分两(三)种(虚实剖分会在 LCT 里面讲,剩下的是重链剖分和长链剖分)

    公共套路

    对于每个节点,钦定 一个儿子当“重儿子”,然后每个点和重儿子形成的链叫“重链”,其它的是“轻链”。然后树会变成若干条直链拼一起,于是把树上的路径问题变成了序列上的问题。

    重链剖分

    重链剖分用于在 (O(log n)) 的时间里拆分任意的链。它适合套个线段树,主席树,线性基,堆等能合并的数据结构,来解决树上的路径问题。

    它选重儿子的方法是:(size) 最大的是重儿子。“容易证明”,这样的话,每一条路径都顶多会被拆成 (O(log n)) 条链。

    证明:

    不是我口胡,真的好证。对于每条路径 ((u,v)),我们把它拆分成 ((u,LCA)+(LCA,v))。然后现在就变成了一条直链,证明它最多会被划分成 (log) 条重链。继续观察,我们发现:一条链被划分成的重链数=它经过的轻边数+1(因为轻边是划分重链的)。那我们只要考虑它经过多少轻边。

    对于一个点 (u),我们走一条轻边到 (v),它的 (size) 至少会除以 (2)。反证:如果 (size[v]>size[u]/2),那么显然 (v) 是重儿子,然而我们设 ((u,v)) 是轻边,矛盾。所以这个结论成立。

    那么一条链经过的轻边数就是 (O(log n)) 的。两条加一起就是多一个常数,还是 (log) 的。

    然后就非常的 simple 了,只要序列上能做的问题,就能多一个 log 的时间放到树上。

    当然,有些问题要注意方向。比如求路径上矩阵的乘积,这种记得多讨论亿点细节。

    还有,在一个不是刻意构造的树上,一般来讲重链剖分常数是很小的。那就可以考虑重链剖分求 LCA。尽管代码比倍增长不少,但是它很快。

    长链剖分

    长链剖分用来不带 (log) 的解决只跟深度有关的问题。

    可以想象一下,它要是也是 (log),那还不如写重链剖分;它既然被发明出来,那它一定有它独一无二之处。它就是那种,在一个小问题上做到极致的算法。

    不废话了,讲。

    和重链剖分只有一个不同,就是它定义重儿子为最 "深" 的儿子。最“深”的儿子,形式化的,就是点 (vin son(u)),使得它的 (d_{max}) 最大,其中 (d_{max}(u)=max{dep[v] | vin subtree(u)})

    在讲到长链剖分的时候,一般管重链叫“长链”。

    长链剖分基本性质:如果 (x)(y)(k) 级祖先,那么 (x) 所在的长链长度肯定 (>=k)

    这个性质yanQval直呼显然,因为如果 (x) 所在的长链长度 (<k),那把它换成 (<x,y>) 这条链,就会更长,与长链的定义矛盾。

    然后看它能干啥。

    1. (O(1))(k) 级祖先(预处理有 (log),查询没有)

      首先要预处理倍增数组。

      然后还要预处理一个东西:对于每条重链的链顶 (t),假设链长度为 (d),预处理出 (t) 往上往下 (d) 个位置,用 vector<> 存。显然,所有链的长度加起来是 (n)。所以这一步的占用空间是 (2n)。不会炸。

      然后还要预处理出 (log_2) 数组(也就是二进制最高位)

      然后开始查询。

      先找到最大的 (x) 使得 (x<=k) 并且 (x) 是二的幂(查 (log_2) 数组)。用倍增数组跳上去。

      设跳到了 (x')。根据基本性质,(x') 所在的长链长度肯定 (>=x)。由于 (x) 是最大的二次幂,所以 (x>k-x)。于是 (x') 所在的长链长度肯定 (>k-x)。然后查表就行了。

    2. 线性存储每个点跟深度有关的数组

      做树上问题会经常遇到一个数组:(f_{u,i}) 表示 (u) 的子树中和 (u) 距离 (i) 的 xxxx。

      然后考虑把所有的 (f) 存在一个内存池里,然后 (f_u) 可以直接继承其重儿子,对于轻儿子,就更新一下就好了。

      关于如何继承:设 (u) 的重儿子是 (s),那么 (f_{u,i}=f_{s,i-1})

      如果 (f_u) 在内存池中的指针是 (p),直接令 (f_{s}=p+1) 即可。

      然后对于 (u) 的每个轻儿子 (v)(f_{u,i}+=f_{v,i-1})

      显然这里 (i) 枚举的范围是:轻儿子所在的重链长度

      所有重链长度和为 (n)。所以这样做复杂度是 (O(n)) 的。然而实际存储似乎需要四倍常数。不知道咋整的。

    虚实剖分

    它是一个动态钦点重边的剖分。

    先不看下去,就看“动态”二字,想象一下它在干什么。然后就可以去看 LCT 一章了!

  • 相关阅读:
    linux定时备份数据库
    svn 没有绿色小勾
    oracle dblink ora-12541 无监听
    WPS 设置多级标题
    sql 提高速度方法
    领骑衫获奖感言与总结
    团队项目(任务五):项目回顾
    项目回顾
    团队项目(任务四):第二次冲刺
    团队项目(任务三):第一次冲刺
  • 原文地址:https://www.cnblogs.com/LightningUZ/p/14730566.html
Copyright © 2011-2022 走看看