Link-Cut Tree学习笔记

Link-Cut Tree学习笔记

自己好菜啊,省选之前啥算法都不会,考试把一个(nlog n)的LCT题硬是写了个(nlog^2{n}).但是跑过去就对了.

所以现在要来重新学习一下( ext{Link-Cut Tree}).

定义

一个资磁动态加边,删边,维护信息的数据结构,叫做(LCT).

前置知识

Splay

Orz yyb

实链剖分

讲道理我真的不知道实链剖分改叫什么名字.

你考虑对于原来树上面的非叶子节点,指定一个实儿子,其他的都是虚儿子.

与重链剖分和长链剖分不同,实链剖分需要比较快捷的维护,这个时候我们就需要用到( ext{splay}).

性质

  1. 每一个( ext{splay})维护的是原树上的一条实链,而且中序遍历按照深度排序.
  2. 每一个节点只会出现在一个( ext{splay})里面.
  3. 实边的儿子会给儿子节点,虚边的儿子只是指向父亲.

操作

前置操作

你需要资磁( exttt{pushup,pushdown,reverse,rotate})的操作,这里不再赘述,和( ext{splay})维护数列一样.

access操作

首先是一个贼牛逼的操作,考虑我们现在要把一个点到根节点路径上的所有边都变成实链,也就是让路径上的点在一个( ext{splay})里面,代码如下:

void access(int x)
{
    for(int y=0;x;y=x,x=t[x].ff)
    {	
        splay(x);t[x].ch[1]=y;pushup(x);
    }
}

我们考虑如何修改实链,首先你需要把(x)变成(splay)的根,以维护第一个性质,不然你改了右儿子但是这个右儿子实际上应该是在父亲的之类的.

然后因为(y)的深度比(x)大,所以要放在右儿子位置.

最后改了儿子,然后(pushup)一下.

findroot操作

int findroot(int x)
{
	access(x);splay(x);
	while(t[x].ch[0])x=t[x].ch[0];
	return x;
}

考虑找到它对应的根,等于是我们把他到根的路径(access)出来,然后把他变成(splay)的顶端,接着考虑一直往左儿子走,这样子走就是不断把深度变大.

makeroot操作

void makeroot(int x){access(x);splay(x);reverse(x);}

这个时候我们要把(x)变成根节点,然后重新定义深度.考虑先把(x)节点(access)上去然后(splay),此时在(x)左边的就是原本深度比(x)大的,右边因为(access)上去了,所以没有右儿子.考虑把儿子(reverse)一下,这个时候就满足了我们的要求了.

split操作


我们需要资磁把两个节点的路径抠出来,那么我们可以把一个变成根节点,另一个(access+splay)即可.

Link操作

直接一个(makeroot)然后连成另一个的虚儿子即可.

Cut操作

首先可以把两个的路径(split)出来,此时如果两个点有直接连边的话,显然是(y)的左儿子就是(x),这个时候我们直接断掉对应关系即可.

Link+Cut代码

void Link(int x,int y){makeroot(x);t[x].ff=y;}
void Cut(int x,int y)
{
	split(x,y);
	t[y].ch[0]=t[x].ff=0;
}

这就是(LCT)的基本操作.

维护子树信息

(lct)还可以维护子树信息,这里的子树可以是实子树,也可以是虚子树.

  • 如果是维护实子树信息,你需要多写一个(pushup),(splay)里面多存一个变量表示子树的信息.

  • 如果是维护虚子树信息,你需要在(access)的时候加上原来的右子树,即把原来的边变成虚边然后加入贡献,新的改成实边的删除贡献.

未完待续

题目会慢慢补的.确信

原文地址:https://www.cnblogs.com/fexuile/p/13035190.html