學習LCT小結

前前言

話說前面部分大概是我一年前搞的吧。
最近又複習了一哈LCT,那就補補坑吧。
注意,爲了區分,從我寫spaly開始都是現在寫的。

前言

首先,LCT是什麼呢?
我們看看百度百科——

LCT即自動細胞學檢測系統,又稱液基細胞學檢測系統。是宮頸篩查的一種方法。

疏鬆結締組織(loose connective tissue)又稱蜂窩組織(areolar tissue),其特點是細胞種類較多,纖維較少,排列稀疏。疏鬆結締組織在體內廣泛分佈,位於器官之間、組織之間以至細胞之間,起連接、支持、營養、防禦、保護和創傷修復等功能。

好吧,不是這個。
LCT即爲link-cut-tree
是一種解決動態樹的有效利器。
動態樹是一類問題,常見的有維護樹的連通性,求樹上路徑的極值等。

然後LCT即爲一種用Splay來維護樹鏈剖分的算法,簡單理解爲“支持刪邊、加邊的動態樹鏈剖分”

前置技能:

樹鏈剖分https://blog.csdn.net/HiChocolate/article/details/77170675(不會的可以看看我寫的)
Splay https://blog.csdn.net/qq_30974369/article/details/77587168(不會的可以看看不是我寫的)
重點是樹鏈剖分的全部內容加上splay的一些基本操作(如標記下傳、區間翻轉《排序機械臂》)

一些定義

既然百度百科沒有很好的定義來學習,那我就來大致講講。(實際上是我懶得打定義)

LCT的本質也是樹鏈剖分,它的樹也是用很多的偏愛路徑和非偏愛路徑來連接起來的。
這些爲了方便理解,不妨也用樹鏈剖分的定義來定義。
1、重邊連起來會組成重鏈,重鏈之間沒有公共點。
我們首先跑一遍算出dfn序之後,我們就可以找到很多很多條重鏈。
2、那麼對於一顆樹,其中一條重鏈的點,都在一顆splay中,關鍵字是深度。
3、樹鏈剖分的重鏈是一成不變的,但是LCT的重鏈是可以隨意改變的。這也註定了LCT的重鏈需要用到splay來維護。
4、重鏈與重鏈之間的邊在splay中用虛邊來表示。
5、一條重鏈的虛邊會存在它的splay的根上,指向這條重鏈的頂點的father所在的重鏈的splay的根。

就是這麼多啦。
其實這樣子很難理解4與5的定義什麼意思,那麼我們畫畫圖。
這裏寫圖片描述
上面加粗的邊即爲重邊,然後連成重鏈。
然後,我們把每一條重鏈都給弄出來,對它建一顆splay,那麼就變成——
這裏寫圖片描述
那麼對於上面的這個圖,三角形就代表一個splay,每個splay對應着LCT上的每個鏈。
那麼有些鏈是用一條輕邊連起來的,那麼在splay上就用一個虛邊連接。
理解定義5,就會發現在這張圖上,這些有向邊可以表示爲虛邊。

然後就大致介紹完LCT的基本定義與構造了。

一些乾貨

由於我們的這個LCT並不需要把它建出來,只需要維護spaly即可。
因此,我們節點維護下面的一些基本值:

  • fa表示當前節點spaly上的父親
  • pf表示當前節點在LCT上虛邊連接的點
  • son[1/0]在spaly上的兒子

關於spaly

其實spaly在LCT中只需要利用其旋轉,就可以基本完成一切操作(廢話)
那麼在旋轉過程中,需要改進的只有一個向虛邊傳送。
代碼

void rotate(int x)
{
	int y=t[x].fa,k=get(x);
	if(t[y].fa) t[t[y].fa].son[get(y)]=x;
	if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
	t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
	update(y),update(x);
	t[x].pf=t[y].pf;//就在這裏!!!
}

核心操作access

我們定義一個操作access(x)access(x)表示把當前x到根的所有邊都變成重邊,然後把這條鏈以外的所以邊變成輕邊。
具體操作看圖:
(盜盜圖)
在這裏插入圖片描述
應該很好理解。
代碼:

void access(int x)
{
	for (int y=0;x;update(x),y=x,x=t[x].pf)//一直跳重鏈
	{
		splay(x,0);
		t[t[x].son[1]].fa=0;//斷掉原來的重鏈
		t[t[x].son[1]].pf=x;
		t[x].son[1]=y;//把下面的spaly接到上面
		t[y].fa=x;
		t[y].pf=0;
	}
}

基本操作1 makeroot

我們定義一個操作makeroots(x)makeroots(x)表示把x變成LCT的根。
操作簡單,先access一下,那麼我們就可以得到一顆spaly。
然鵝這顆spaly最右邊的點是x,我們要把他變成最左邊。
於是把它旋轉到根,翻轉一下即可(翻轉就是排序機械臂中的翻轉)
代碼:

void make(int x)
{
	swap(t[x].son[0],t[x].son[1]);
	t[x].tag^=1;
}
void makeroot(int x)
{
	access(x),splay(x,0);
	make(x);
}

基本操作2 findroot

我們定義一個操作findroots(x)findroots(x)表示尋找x所在樹的root。
這個操作有什麼用呢?
你可以用這個代替並查集,只是時間多了些
用醋很多,就不一一介紹了。

如何實現?
access(x)一下,然後我們發現x和原本的根在同一spaly上。
然後詢問spaly上最左邊的點即可。

int findroot(int x)
{
	access(x),splay(x,0);
	for (;t[x].son[0];x=t[x].son[0]);
	return x;
}

基本操作3 link

這個就很帥氣了。
我們定義一個操作link(x,y)link(x,y)表示連接x和y這條邊。
方法很簡單,我們先makeroot(x),然後把x連接到y即可。

void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) t[x].pf=y;
}

基本操作4 cut

我們定義一個操作cut(x,y)cut(x,y)表示刪掉x和y這條邊。
我們考慮makeroot(x),然後access(y)。
如果x和y之間有連邊,那麼y必定是x的兒子。
斷掉即可。

void cut(int x,int y)
{
	makeroot(x),access(y),splay(y,0);
	if(t[x].fa==y) t[y].son[get(x)]=t[x].fa=t[x].pf=0;
}

應用

大致就是上面的這些操作。
例題?洛谷上很多。
當然,https://blog.csdn.net/HiChocolate/article/details/101380259
這也是一個模板。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章