Link-Cut Tree,用来解决动态树问题。

宏观上,LCT维护的是森林而非树。因此存在多颗LCT。有点像动态的树剖(链的确定通过$Access$操作),每条链用一颗$splay$维护。$splay$维护链的关键字是深度,因此一条链的顶端就是$splay$中键值最小的点

由于LCT的资料有很多,在此不详细阐述。只是谈谈理解有困惑的几个点,其中大多已经解决了:

关于虚边

这里的虚边其实不能算叫边,只能说是一个父指针。由于splay是二叉树,但有可能出现有好多虚边指向一个点的情况。因此虚边其实就是儿子认爸爸,而爸爸不认儿子

虚边的真正作用其实是连接每颗splay,换句话说,虚边其实就是每颗splay根节点的父指针,指向另一个splay中的节点。然而这个父指针并不会因为rotate而被破坏,因为这里和普通的splay不同,在rotate的过程是有可能涉及到别的splay中的点(也就是gf),在rotate时,我们一定会把原本指向gf的节点f改为x

关于$Access$操作

Access操作用来让一个节点x到当前LCT的根节点的整条路径都变为实边(并且节点x与下方的实边断开,成为最底部的点)。形象的说,由于每个点只能在一个splay中,有的实边要变为虚边。Access操作时LCT动态维护森林的根本,而之所以能够达到动态效果正是由于实链的不断变换

因此我们可以从x开始向上走,每一次将当前节点(一定是当前splay中深度最大的)splay到根,由于它是深度最大的,一定没有右儿子。因此可以把它的右儿子接上上一轮的splay(第一轮就接上空的)。注意这里的接儿子只需要改变ch的值,而不用更新fa(因为原本是虚边,儿子一定认爸爸)。如果它本身就有右儿子也不管,这样接上另一个儿子之后,原来的儿子依然认它为爸爸,然而爸爸却突然之间不认它了,因此就变成了虚边。

关于$Makeroot$操作

意义就是让节点x成为当前splay的根。因此可以先Access(x),然后splay(x)(注意splay不会改变任何关系)。此时由于x已经被Access了,因此一定是深度最大的节点。所以splay之后一定没有右儿子。此时想让x成为根,那么也就是深度最小。开始发挥splay最重要的作用——翻转。

说到关于懒标记的,splay中标记的下传可以这样操作(新学):对于要splay的节点x,先一路递归到达根节点,然后依次往下下传标记。因为splay只会一路影响它的father,update也是一个原理

问题:为什么要整个翻转,而不是只交换根的左右儿子呢?效果貌似一样?虽然实测爆0,但还是不太懂为什么……(我太弱了)

关于$Findroot$操作

找根。最简单的操作之一,先Access和splay,此时最小的便是根

关于$split$操作

提取出x到y的链。只需要makeroot(x),然后access(y)。此时就打通了x到y的路径(真的好动态哇)。为了方便查找,splay(y),这样从y一直找最小的就好了。也是灰常简单滴

关于$Link$操作

makeroot(x),然后把x的父亲置为y

为什么?为什么?为什么?

Update:我可能脑子有点问题…… 其实把x的父亲置为y相当于连了一条虚边——此时他们合并为了一个LCT,其中x就在y的下面了,和正常的一样

关于$Cut$操作

依旧不难。要断x到y的直接边,那么首先要把它提取出来,然后断了就好了。如何提取?split啊!!!

但是注意如果要判断这条边是否存在有两个条件,其一很简单,$ch[y][0]==x?$。因为前面split的时候y被作为了splay的根节点,而因为它们是直接边,所以高度差不超过1.因此就是左儿子

还有一个条件是要满足$ch[x][1]==0$,因为如果x存在右儿子,这个右儿子肯定比它大比y小,这样差值就不是1了

口诀

旋转记得判父亲,伸展记得先下传。

Access记得要更新,Link记得要mroot。

  1. /*By DennyQi 2018*/
  2. #include <cstdio>
  3. #include <queue>
  4. #include <cstring>
  5. #include <algorithm>
  6. using namespace std;
  7. typedef long long ll;
  8. const int MAXN = ;
  9. const int INF = 0x3f3f3f3f;
  10. inline int Max(const int a, const int b){ return (a > b) ? a : b; }
  11. inline int Min(const int a, const int b){ return (a < b) ? a : b; }
  12. inline int read(){
  13. int x = ; int w = ; register char c = getchar();
  14. for(; c ^ '-' && (c < '' || c > ''); c = getchar());
  15. if(c == '-') w = -, c = getchar();
  16. for(; c >= '' && c <= ''; c = getchar()) x = (x<<) + (x<<) + c - ''; return x * w;
  17. }
  18. int N,M,opt,x,y;
  19. int val[MAXN],ch[MAXN][],fa[MAXN],xr[MAXN],tag[MAXN];
  20. struct LinkCutTree{
  21. inline bool rson(int f, int x){
  22. return ch[f][] == x;
  23. }
  24. inline bool isroot(int x){
  25. return ch[fa[x]][rson(fa[x],x)]!=x;
  26. }
  27. inline void update(int x){
  28. xr[x] = val[x];
  29. if(ch[x][]) xr[x] ^= xr[ch[x][]];
  30. if(ch[x][]) xr[x] ^= xr[ch[x][]];
  31. }
  32. inline void rotate(int x){
  33. int f = fa[x], gf = fa[f];
  34. bool p = rson(f,x), q = !p;
  35. if(!isroot(f)) ch[gf][rson(gf,f)] = x; fa[x] = gf;
  36. ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
  37. ch[x][q] = f, fa[f] = x;
  38. update(f), update(x);
  39. }
  40. void prepare(int x){
  41. if(!isroot(x)) prepare(fa[x]);
  42. if(!tag[x]) return;
  43. tag[x] = ;
  44. swap(ch[x][], ch[x][]);
  45. tag[ch[x][]] ^= , tag[ch[x][]] ^= ;
  46. }
  47. inline void splay(int x){
  48. prepare(x);
  49. while(!isroot(x)){
  50. int f = fa[x], gf = fa[f];
  51. if(isroot(f)){
  52. rotate(x);
  53. break;
  54. }
  55. if(rson(gf,f) ^ rson(f,x)) rotate(x); else rotate(f);
  56. rotate(x);
  57. }
  58. }
  59. inline void access(int x){
  60. for(int y = ; x; y = x, x = fa[x]){
  61. splay(x), ch[x][] = y, update(x);
  62. }
  63. }
  64. inline void mroot(int x){
  65. access(x);
  66. splay(x);
  67. tag[x] ^= ;
  68. }
  69. inline int findroot(int x){
  70. access(x);
  71. splay(x);
  72. while(ch[x][]) x=ch[x][];
  73. return x;
  74. }
  75. inline void split(int x, int y){
  76. mroot(x), access(y), splay(y);
  77. }
  78. inline void link(int x, int y){
  79. if(findroot(x) == findroot(y)) return;
  80. mroot(x);
  81. fa[x] = y;
  82. }
  83. inline void cut(int x, int y){
  84. split(x, y);
  85. if(ch[y][] != x || ch[x][] != ) return;
  86. ch[y][] = fa[x] = ;
  87. }
  88. inline void change(int x, int y){
  89. mroot(x);
  90. val[x] = y;
  91. update(x);
  92. }
  93. }qxz;
  94. int main(){
  95. // freopen(".in","r",stdin);
  96. N = read(), M = read();
  97. for(int i = ; i <= N; ++i) val[i] = read();
  98. for(int i = ; i <= M; ++i){
  99. opt = read(), x = read(), y = read();
  100. if(opt==) qxz.split(x,y), printf("%d\n",xr[y]);
  101. if(opt==) qxz.link(x,y);
  102. if(opt==) qxz.cut(x,y);
  103. if(opt==) qxz.change(x,y);
  104. }
  105. return ;
  106. }

「Link-Cut Tree」学习笔记的更多相关文章

  1. Note -「Dsu On Tree」学习笔记

    前置芝士 树连剖分及其思想,以及优化时间复杂度的原理. 讲个笑话这个东西其实和 Dsu(并查集)没什么关系. 算法本身 Dsu On Tree,一下简称 DOT,常用于解决子树间的信息合并问题. 其实 ...

  2. Note -「圆方树」学习笔记

    目录 圆方树的定义 圆方树的构造 实现 细节 圆方树的运用 「BZOJ 3331」压力 「洛谷 P4320」道路相遇 「APIO 2018」「洛谷 P4630」铁人两项 「CF 487E」Touris ...

  3. 「快速傅里叶变换(FFT)」学习笔记

    FFT即快速傅里叶变换,离散傅里叶变换及其逆变换的快速算法.在OI中用来优化多项式乘法. 本文主要目的是便于自己整理.复习 FFT的算法思路 已知两个多项式的系数表达式,要求其卷积的系数表达式. 先将 ...

  4. Note -「矩阵树定理」学习笔记

      大概--会很简洁吧 qwq. 矩阵树定理   对于无自环无向图 \(G=(V,E)\),令其度数矩阵 \(D\),邻接矩阵 \(A\),令该图的 \(\text{Kirchhoff}\) 矩阵 \ ...

  5. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  6. 学习笔记:Link Cut Tree

    模板题 原理 类似树链剖分对重儿子/长儿子剖分,Link Cut Tree 也做的是类似的链剖分. 每个节点选出 \(0 / 1\) 个儿子作为实儿子,剩下是虚儿子.对应的边是实边/虚边,虚实时可以进 ...

  7. 【Java】「深入理解Java虚拟机」学习笔记(1) - Java语言发展趋势

    0.前言 从这篇随笔开始记录Java虚拟机的内容,以前只是对Java的应用,聚焦的是业务,了解的只是语言层面,现在想深入学习一下. 对JVM的学习肯定不是看一遍书就能掌握的,在今后的学习和实践中如果有 ...

  8. 「ExLucas」学习笔记

    「ExLucas」学习笔记 前置芝士 中国剩余定理 \(CRT\) \(Lucas\) 定理 \(ExGCD\) 亿点点数学知识 给龙蝶打波广告 Lucas 定理 \(C^m_n = C^{m\% m ...

  9. Link Cut Tree 总结

    Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...

随机推荐

  1. Go+Python双剑合璧

    目的 Python调用Go的方法,Python有很多功能强悍又使用简洁的库.而新生军Go的多核心利用率也是非常强悍的.当然这是明面上的优点.反正你有很多理由想要让Python能够调用Go的方法. 实验 ...

  2. H5 60-浮动元素排序规则

    60-浮动元素排序规则 <!DOCTYPE html><html lang="en"><head> <meta charset=" ...

  3. (第十三周)Final Review会议

    项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 Final Review会议 时间:2016.12.2   13:00——15:00 地点:冬华楼一楼大厅 会 ...

  4. AtCoder Beginner Contest 116 D - Various Sushi (贪心+栈)

    D - Various Sushi Time Limit: 2 sec / Memory Limit: 1024 MB Score : 400400 points Problem Statement ...

  5. mybatis的mapper注入失败

    因为处在两个不同的资源文件夹下: 导致classpath无法加载其中一些文件,所以修改为classpath*后顺利进行. <!-- 加载spring容器 --> <!-- neede ...

  6. 项目集成自动分词系统ansj,实现自定义词库

    一,分词系统地址:https://github.com/NLPchina/ansj_seg 二,为什么选择ansj? 1.项目需求: 我们平台要做手机售后的舆情分析,即对购买手机的用户的评论进行分析. ...

  7. HDU 2003 求绝对值

    http://acm.hdu.edu.cn/showproblem.php?pid=2003 Problem Description 求实数的绝对值.   Input 输入数据有多组,每组占一行,每行 ...

  8. WebSocket推送

    本篇博客只是记录websocket在自己的项目中的应用,只是记录,不做说明(后来替换为GoEasy了). /** * 握手的设置,这其实是为了获取session */ public class Get ...

  9. Windows 禁止 chrome 自动更新的方法

    来源: https://www.cnblogs.com/zhouwanqiu/p/9329134.html 公司进行兼容性测试 需要chrome的浏览器版本, 但是 公司网络抽风 偶尔 总能是升级到最 ...

  10. vue中的跨域问题

    https://segmentfault.com/a/1190000011072725(原文)   使用vue-axios和vue-resource解决vue中调用网易云接口跨域的问题 注(api很重 ...