zoukankan      html  css  js  c++  java
  • 长链剖分学习笔记

    关于长链剖分

    长链剖分,也属于树链剖分的一种方式,但是其与经典的重链剖分又不太一样。
    在重链剖分中,我们评判儿子重或轻的方式是比较其子树节点数量
    在长链剖分中,我们以子树中最深的叶节点深度的大小来比较。
    其他步骤与重链剖分类似,都是两遍DFS即可解决,都是O(n)的复杂度。


    接下来,我们来看两个长链剖分的经典问题.

    1.询问某个点的k级祖先

    时间复杂度要求: O(nlogn).

    需要技能:

    • 长链剖分
    • 倍增

    为什么要长链剖分:

    我们利用的性质是:任意一个点的k级祖先所在链的链长一定大于等于k。

    证明:

    1.如果这个点和他的k级祖先在一条链上,那么结论显然成立。

    2.如果不在一条链上,那么一开始这个祖先没有选择这棵子树是因为别的子树更深,故而所在链一定是大于k的,结论仍然成立。


    操作步骤

    • 预处理
    1. 对树进行长链剖分,记录每个点的链头和深度
    2. 倍增预处理出每个点2^n级祖先
    3. 如果某条链的长度是len,那么在链头处记录链头向上的len个祖先,并记录向下的len个链的元素
    4. 记录每个数最高位1是多少
      总时间复杂度: O(nlogn)
    • 查询
    1. 先利用倍增数组跳K的最高位的1层,设剩余层数为k,可以发现

    2. 利用之前证明的性质,我们可以发现当前节点所在链链长一定严格大于k。如果链头在当前节点的k级祖先上面,那么我们利用链头向下的数组可以O(1)得到答案,否则利用向上的数组同样可以O(1) 得到答案。


    2.统计每个点子树中以深度为下标的可合并信息

    这个应用的思想十分类似Dsu on tree
    思想是当前结点直接继承重儿子的信息,轻儿子信息暴力统计。
    因为长链剖分的特殊性,因此这个应用仅限于以深度为下标。
    更一般的就只能使用Dsu on tree做到O(nlogn)的复杂度了。

    更具体地:
    若需求出点x的信息,首先从重儿子继承。
    因为以深度为下标,因此只需要对数组进行移位即可。
    后面我们会提到移位的实现方法。

    接着,轻儿子的信息暴力合并到当前的信息中。

    时间复杂度O(n)。
    分析如下:
    每个点x只会暴力统计其所有轻儿子的信息,而每个轻儿子的信息大小为该轻儿子所在长链长度。
    而当递归到x的父节点fa(x)时,若xx不是fa(x)的重儿子,则fa(x)会暴力统计大小为x长链长度的信息。
    故,每个长链只会对转移的复杂度做一次大小为其长度的贡献。
    因此根据性质1,总时间复杂度为O(n)

    移位的实现方法
    本处仅提到一种实现方式,但方法不只有这一种。
    用指针维护动规数组,在重儿子传递给父亲时将指针左移/右移一位。(左移还是右移根据转移方程决定)
    而给其他轻儿子分配不相交的内存即可。

    不难发现,这样实现的空间复杂度仍为O(n)。
    特别注意,这样实现也有相应的缺点,即若动规的数组高于一维,那么数组是一次性的,不能在完成转移后查询中间过程的值。
    原因是部分内存被共用掉了。

    小结

    长链剖分在维护一些与深度有关的信息时十分强力有效。
    在考虑题目时灵活运用其性质往往可以得到较好的结果。


    例题:

    Written By @Kevin_Naticl

  • 相关阅读:
    MiniUI破解方法
    mysql [索引优化] -- in or替换为union all
    MySQL匹配指定字符串的查询
    MySQL优化之like关键字
    Java身份证归属地目录树
    JS数字指定长度不足前补零的实现
    jQuery Distpicker插件 省市区三级联动 动态赋值修改地址
    JS 正则表达式从地址中提取省市县
    Eclipse/myEclipse 代码提示/自动提示/自动完成设置
    Spring Mvc配置多视图
  • 原文地址:https://www.cnblogs.com/Kv-Stalin/p/9246578.html
Copyright © 2011-2022 走看看