树链剖分+LCT

前言

  • 填了一个巨坑,然而还有很多巨坑要填
  • 本片主要内容为LCT+树链剖分

引子

  • 有一类问题,要求在一个序列中做区间修改,区间查询
  • 可以用线段树解决这一类问题
  • 有另一类问题,要求在一个序列中做区间修改,区间查询,还要求插入删除,区间反转,区间循环移动等等坑爹的操作(维修数列)
  • 可以用splay来解决这一类问题
  • 现在我们要把这些操作拓展到树上

树链剖分

给定一棵形态不变的树,要求支持查询一条链,修改一条链

0x00 基本思想

  • 序列(只有一条链)上的问题我们已经可以解决
  • 我们尝试把这个树拆成很多条链来解决
  • 一种有效的办法是随机剖分(期望log)
  • 更稳定的办法是轻重链剖分

0x01 轻重链剖分

  • 定义一个节点的size是以这个节点为根的子树中的节点数
  • 每个节点与其size最大的儿子的连边定义为重边,其他为轻边
  • 重边构成的极大链被称为重链(应该都知道极大和最大的区别吧?)
  • 如果用线段树维护每条重链,那么对于每次查询,重链上的复杂度是 重链段数*log
  • 那么重链段数最多有多少呢?
  • 容易发现重链数=轻边数+1
  • 而且每经过一条轻边,子树大小都至少翻倍(类似于启发式合并)
  • 所以轻边数是log级别的
  • 那么重边数也是log级别的
  • 那么每次查询就是log^2级别的
  • 所以总复杂度是$ O(qlog^2{n}) $

0x02 具体实现

  • 第一次dfs求出每个节点的size,depth,father,重儿子
  • 第二次dfs先走重儿子,求出每个节点的dfn和top(这个节点所在的重链顶端)
  • 查询(修改类似)的时候
  1. 两个节点的top不同,goto 2,否则goto 3
  2. 让深度大的一个节点跳到它top的father并且在答案中更新这一段,goto 1
  3. 在线段树上查询两个节点的中间一段,结束查询

0x03 Extra

  • 如何维护链同时支持子树查询?
  • 发现虽然第二次dfs改变了dfs序,但是一个子树中的dfn还是连续的一段
  • 在每个节点中维护一下这个节点为根的子树中最大的dfn,修改比链还要简单

0x04 模板题

  • 软件包管理器,真·模板题,兼顾子树查询和链查询

LCT

给定一棵形态变化的树,要求支持查询和修改一条链

0x00 基本思想

  • 形态变化那只好splay啦
  • 还是维护一些链
  • 只不过因为形态会变化,不能轻重链剖分了
  • 运用splay的思想,splay把最近操作过的节点splay到根,在LCT中我们把最近操作过的节点到根的路径剖成一条链
  • 用splay维护一条链,这个splay叫做auxiliary tree

0x01 一些定义

  • father:如果一个点不是所在splay的根,那么这个father指的是auxiliary tree中的father(即与普通splay中的father含义相同),如果这个点是所在splay的根,那么这个点的father指的是splay中深度最小的节点的真·父亲(即原树上的父节点)
  • child:splay中的孩子,不维护原树上的孩子
  • 偏爱儿子:最后一次被access过的儿子(在access路径上的儿子),特别的,如果access的节点是p,那么p没有偏爱儿子

0x02 基本操作

splay的操作

  • 要注意整个LCT实际上在实现的时候只用一棵splay维护,并不是很多很多splay
  • 注意LCT中的father和普通splay中的father有区别,在旋转的时候要注意改儿子的问题(一个节点可能不是他的father的child)

access

  • 把一个节点p到根的路径变成偏爱路径
  • 首先p不能有偏爱儿子了,把p splay到根,断开右儿子
  • :loop
  • 把p的father splay到根,把p接到它father的右儿子上
  • 如果还没到根,p=p->father,goto loop

make_root

  • 把p变为LCT的根
  • 异常简单
  • access(p)
  • splay(p)
  • 给p打一个翻转标记
  • 原理:实际上p到根的路径上深度都反过来了,其他部分树的形态并未改变,加个翻转标记即可
  • 在p,q两个节点间加一条边(保证仍是一个森林)
  • make_root(p)
  • p->father=q

cut

  • 把p,q间的边断掉
  • make_root(p)
  • access(q)
  • splay(q)
  • 断掉q的左儿子即可

链上修改

  • make_root(p)
  • access(q)
  • splay(q)
  • 对q像splay那样打标记就行了

维护联通性

  • 暴力p=p->father,看最后是否相同

0x03 假如我非要加入子树操作呢

  • 不好意思LCT不能同时支持子树修改和子树查询
  • 仅支持子树查询可以维护虚边信息
  • 仅支持子树修改非常难搞,我也不会写

0x04 假如我非要维护边的信息呢

  • 懒办法:把一条边拆成两条边一个点,在点上维护信息

0x05 模板题

  • 洞穴勘测
  • 弹飞绵羊

总结

  • 比想象中的好写多了。。。
许愿弥生改二
原文地址:https://www.cnblogs.com/LoveYayoi/p/6745541.html