LCT

LCT学习笔记

摘要:肝了大半天的内容,属于任性。

splay

maintain

就是维护size罢了。

get

返回是父亲的左儿子还是右儿子。

clear

多测清空你都会,不信这个你不会。

rotate

是和Treap的操作一模一样的吧,,不过是站在儿子的角度旋转的吧。
为了避免出错:记住以下需要更新的内容(y为x的父亲,z为y的父亲):

  • y的同侧儿子
  • x的异侧儿子的父亲
  • x的异侧儿子
  • y的父亲
  • z的儿子
  • x的父亲

以上两两是一对。
记住特判x的异侧儿子和z不存在的情况。

splay

体谅一下作者祭天的绘图水平
TYLAKIOI
到处splay(x)邪教是绝对正确的!!!

简单来说:如果父亲和爷爷(先姑且这么叫)在一条直线上,那么先换父。否则先换子。
下方是OI-wiki上现有的代码。

void splay(int x) {
  for (int f = fa[x]; f = fa[x], f; rotate(x))
    if (fa[f]) rotate(get(x) == get(f) ? f : x);
  rt = x;
}

代码压行到了丧心病狂的程度:一次循环里,先把 \(f\) 赋值为 \(fa_x\) 检验其是否存在。如不存在,退出循环。再判断是否存在爷爷,如果不存在只单旋,否则按上方规则双旋。

势能分析

建议别看,全是贺的。

这里是题外话:为了证这个复杂度,我先是在OI-wiki上看,但Oiwiki不靠谱的天然特性使得我成功的没有看懂。然后 \(\textcolor{black}{\_}\textcolor{red}{lazytag\_}\) 给我推荐了这篇博客,虽然写的很好,但是“同理可得”得不了让菜菜的我很心痛。最后我是在这里看懂了式子的推导,但是我对势能分析证复杂度理解不深,想不明白那个 \(\Theta(\log n)\) 的变化量和时间有什么关系,,,最后我自己yy了亿下终于找到了一种合理的解读!incredible!

感觉有希望成为全网最详细的解读了!

\(w_x = \log(size_x)\) ,初始势能 \(\varphi_0 = \sum w_i \leq n \log n\) 。末势能同理。
我们考虑求出一次Splay后 势能变化量和时间消耗的总和。(注意定义)
一次zig:

\[\Delta \varphi = 1 + w'_x + w'_{fa} - w_x - w_{fa} \leq 1 + w'_{fa} - w_x \leq 1 + w'_{x} - w_x \]

\(1\) 为时间消耗,其余部分为势能变化,下同。

一次zig-zig(g为爷爷)

\[\Delta \varphi = 1 + w'_x + w'_{fa} + w'_g - w_x - w_{fa} - w_g \\ \leq 1 + w'_{fa} + w'_g - w_x - w_{fa} \\ \leq 1 + w'_x + w'_g - 2w_x \\ \]

注意:这里的“1”不能省略掉,因为不知道zig-zig的次数是多少。如果不能靠势能内的东西摊掉这个“1”,就会出现“ \(\Theta(n^2)\) 个常数” 的情况。

引理1:\(w_x+w'_g-2w'_x \leq -1\)
证明:

\[w_x+w'_g-2w'_x = \log(\frac{size'_gsize_x}{(size'_x)^2}) \leq \log(\frac{size'_gsize_x}{(size'_g + size_x)^2}) \leq \log(\frac{size'_gsize_x}{2size'_g size_x}) \leq -1 \]

特别说明:第一个不等号请看图理解。
原式加上非负数:

\[\leq 1 + w'_x + w'_g - 2w_x -1 - w_x - w'_g + 2w'_x \\ \leq 3(w'_x - w_x) \]

一次zig-zag

\[\Delta \varphi = 1 + w'_x + w'_{fa} + w'_g - w_x - w_{fa} - w_g \\ \leq 1 + w'_{fa} + w'_g - w_x - w_{fa} \\ \leq 1 + w'_{fa} + w'_g - 2w_x \\ \]

引理2: \(w'_{fa}+w'_g-2w'_x\ \leq -1\)
证明(同样看图理解):

\[w'_{fa}+w'_g-2w'_x = \log(\frac{size'_gsize'_{fa}}{(size'_x)^2}) \leq \log(\frac{size'_gsize'_{fa}}{(size'_g + size'_{fa})^2}) \leq \log(\frac{size'_gsize'_{fa}}{2size'_gsize'_{fa}}) \leq -1 \]

然后如法炮制:原式加上非负数

\[\because 0 \leq -1 + 2w'_x - w'_{fa} - w'_g\\ \leq 2w'_x - w'_{fa} - w'_g + w'_{fa} + w'_g - w_{fa} - w_x \\ \leq 2(w'_x - w_x) \]

所以:一次splay的势能变化量和时间消耗之和是:

\[\Delta \varphi = \Theta(1) + 3(w_{root} - w_{x}) \\ \leq \Theta(\log n) \]

每次在平衡树上的操作的时间消耗和splay的时间消耗是同级的,就可以算进splay的常数里去。

由此可以看到:当时间消耗很大的时候,势能变化量为负

假定操作数量 \(m\)\(n\) 同级,那么总共splay的势能变化量和时间消耗之和是 \(\Theta(n \log n)\),又因为势能变化在 \(\Theta (n \log n)\) 的范围内,所以总时间消耗是 \(\Theta(n \log n)\) 的!完结撒花!

code(文艺平衡树)

LCT

什么?这不是平衡树,这是LCT,遇到蒟蒻变大变高,劝退蒟蒻超好用的。先做做Sone1,再做做动态图连通性。什么?在哪里学?点击下方markdown笔记,学LCT送ETT,超实惠的。

what to solve?

链修链查询,换根换父亲。动态加删边,合并和分离。
更多用法等你发现(雾

peculiarity

  • 虚实链剖分,每棵splay维护一条链,中序遍历splay得到的链的深度单调递增。
  • “认父不认子”,每个点的splay只能访问其一个儿子,其他儿子所属的splay的根节点拉一条虚边指向这个儿子的父亲。

QwQ
QaQ
没错盗图就是快乐。

access

核心操作。LCT要维护的东西是很复杂的,(树的结构整体都在改变),所以LCT的思想是很暴力的,修改或查询一条链,就把它放在一个完整的数据结构里。当然这个数据结构是splay。

从树上拉出一条实链的过程其实不复杂:(摘自) \(\textcolor{black}{f}\textcolor{red}{lashhu}\) 的博客

inline void access(int x){
	for(int y=0;x;y=x,x=f[x])
		splay(x),c[x][1]=y,pushup(x);//儿子变了,需要及时上传信息
}

其中 \(f_x\) 是x在LCT上的虚边父亲。这里边比较玄妙的是 \(f_x\) 也可以表示实边的父亲,但两者的区别在于父亲认不认。所以从代码外观上看不出来是多棵splay。。。

makeroot

比较重要的操作,可以使任意节点成为根,这样任意树上路径都能被拉进一条链。(原本不满足深度递增是不行的。)
非常奇妙。先access(x),这时x是splay中深度最大的点,一定没有右节点了,再翻转整个splay,就是这么离谱。x就成为了深度最小的点了。

findroot

用来判断连通性,使用方法是先access再splay,最后在splay上一直向左走,最后别忘了splay(x)(保证复杂度)。

split

拉出一条(x,y) 的链。。。先makeroot(x),再access(y),最后splay(y)就可以做到访问y来访问整条链。

先makeroot(x),再 \(f_x = y\) ,必要时判一判findroot(y)和x是否相等,还要解释吗?

cut

如果保证断边合法,就先split(x,y)(这时根是y,且splay上只有两个点x和y,直接双向断边),记得maintain。
否则先makeroot(x),再findroot(y)。如果是x,那么就判断是否直接联通。具体方法是看y的父亲是否是x,以及y是否有左儿子(如果有,那么x~y这条边就不是直接相连的。)

splay

这个函数要被魔改一下,因为splay(x)的时候x的祖先可能还有标记没有下放,所以需要人为找到它的所有祖先,再倒序pushdown。复杂度分析参考splay即可。

pushdown

单独拿出来是想说:写代码标准化是一个好习惯。。。
因为我一直写的打标记(无论是线段树还是平衡树)是代表“被修改过,但是子节点没被修改”。
包括平衡树的区间翻转。
似乎在LCT里不这样做就会有奇怪的问题?QwQ。。。

总结:整个算法就是access排列组合,而且暴力到了嚣张的程度,但是由于基于splay,复杂度就是正确的。stotarjanorz
还有什么用FHQ写LCT的人,复杂度是错的吧,为什么还没有人出来制裁AwA。

All the last

说在最后:谢谢你读完了它!
要相信每天都会变强一点点,那么终有一天会站上峰巅。
“实力”是一个玄妙的东西,它确实和你日复一日的练习相关,但是当我问:“你是受了什么启发才做出这道题的”?
你答不上来,你甚至根本没见过类似的题目,但是你就是会做。
相信自己,也要相信魔法啊,我们所过的每个平凡的日常,也许就是连续发生的奇迹。
写代码去了。【挥手】

upd:写完了!1A了!好开心!代码贴在下面。

posted @ 2024-01-25 11:14  cool_milo  阅读(5)  评论(0编辑  收藏  举报