前言
- 填了一个巨坑,然而还有很多巨坑要填
- 本片主要内容为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(这个节点所在的重链顶端)
- 查询(修改类似)的时候
- 两个节点的top不同,goto 2,否则goto 3
- 让深度大的一个节点跳到它top的father并且在答案中更新这一段,goto 1
- 在线段树上查询两个节点的中间一段,结束查询
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到根的路径上深度都反过来了,其他部分树的形态并未改变,加个翻转标记即可
link
- 在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 模板题
- 洞穴勘测
- 弹飞绵羊
总结
- 比想象中的好写多了。。。