lct是動態樹的一種,通過維護實鏈和虛鏈來維護所有路徑之間的關係(類似於樹鏈剖分)。這樣做的目的是為了減少某些鏈上的修改、查詢等操作的複雜度。雖然lct常數巨大。
學lct的大部分都會樹剖吧?我們都知道樹剖維護子樹最大的兒子並形成一條重鏈,由於樹剖是靜態的,所以可以用線段樹來維護。而由於lct需要維護動態的邊,要加邊刪邊。所以需要用更靈活的資料結構來維護,也就是splay(也可以用非旋treap,但是打的人貌似比較少)。
lct將所有的邊分為實邊和虛邊,對於所有的實邊,都有互相連線的邊。而對於虛邊就只有兒子連到父親的邊。我們的splay維護所有的實邊,而剩下的虛邊則可以連線所有的splay。由於我們用splay維護,所以我們可以發現對於所有點x,它的兒子中只有一條實邊。每乙個splay維護的是一條從上到下按在原樹中深度嚴格遞增的路徑,且中序遍歷splay得到的每個點的深度序列嚴格遞增。如圖是一棵splay。接下去lct的講解就以它為例。(紅色為實邊)
lct中有關splay的操作:
void up(int k)
void revers(int k)
void down(int k)
}void rotate(int k)
void splay(int k)
up(k);
}
splay的操作類似於普通的splay,但是又有所不同,比如在進行rotate時我們需要判斷點是否為當前splay的根。而在進行splay操作時,必須時一定要從上往下放標記。
access操作:
access操作是lct的精髓。access(x)的操作是將當前lct的根到x的路徑上的所有邊變為實邊,比如下圖。
我們將從根到黑點的所有邊變為實邊,那麼就必須將一些本來的實邊變為虛邊。
那我們對於每個splay都將x旋轉到根,則x的連向右兒子的邊變為虛邊。並將x的右兒子變成上次access的點,然後上提x(將x變為其父親),繼續進行access操作即可。由於splay的性質,右兒子的深度大於點x,而由於我們已經確定了實邊,剩下的邊都是虛邊,所以將右兒子變為虛邊即可。(刪去x的右兒子,但是fa[右兒子]=x),我們這樣一直往上提,就可以完成access操作。
void access(int k)while(k);
}
makeroot操作:
makeroot(x)操作指的是將點x變為當前lct的根,那我們先access(x),打通x到根的路徑,然後再splay一下,則x就變成了當前splay的根,但是目前的splay是不滿足性質的,因為(當前)深度最小的x在樹根處,而所有的點都是x的左子樹,而實際上所有的點都應該在x的右子樹里(因為深度大於x),所以我們直接將整個區間reverse一下,翻轉一遍就可以滿足splay的性質了。
void makeroot(int k)
findroot操作:
findroot(x)表示x在的splay的樹根。我們先打通x到根的鏈,然後把x旋轉到根,由於我們要找的根深度最小(之前),所以直接一直尋找當前點的左兒子即可。注意在進行findroot時一定要下推標記,不然可能會錯。
int findroot(int k)
split操作:
split(x,y)可以處理出x到y的路徑。我們makeroot(x),然後再access(y),這樣就形成了乙個從x到y路徑的splay,然後我們將y旋轉到根,則y點上的值就是我們要求的路徑的值。
void split(int x,int y)
link操作:
link(x,y)表示連邊(x,y),我們連x和y之前要先判斷一下,如果find(x)==find(y),即x和y在同乙個splay裡,在同乙個聯通塊裡,那麼就直接return,不需要連邊。否則我們先makeroot(x),然後將x的父親變成y即可。
void link(int x,int y)
cut操作:
cut(x,y)表示刪邊(x,y)。我們考慮哪些情況不需要cut,首先如果find(x)!=find(y),肯定不需要cut,因為它們根本不在同乙個聯通塊裡。我們makeroot(x),那麼如果fa[x]!=y那也可以不刪,因為不存在x到y的邊。那如果x與y有邊就一定可以cut嗎?不,如果x有右兒子就不能刪,因為x有右兒子就會意味著x和y之間有別的邊。
void cut(int x,int y)
這些操作做完之後就完成了最基礎的lct操作,下面是luogu上lct模板題的code
#include
#define maxn 300005
using
namespace
std;
int read()
int son[maxn][2],fa[maxn],val[maxn],xyz[maxn],rev[maxn],sta[maxn];
void up(int k)
void revers(int k)
void down(int k)
}int isroot(int k)
void rotate(int k)
void splay(int k)
up(k);
}void access(int k)while(k);
}void makeroot(int k)
int findroot(int k)
void split(int x,int y)
void link(int x,int y)
void cut(int x,int y)
int n,m;
int main()
if(type==1) link(x,y);
if(type==2) cut(x,y);
if(type==3)
}return
0;}
LCT 學習筆記
自己定的學習計畫看起來完不成了 兩天沒學東西,全在補題 決定趕快學點東西 於是就學lct了 link cut tree是一種資料結構,我們用它解決動態樹問題 但是lct不叫動態樹,動態樹是指一類問題 那麼lct的中文名是啥啊 這是 個和 splay 樣只需要寫幾 yi 個 dui 核心函式就能實現一...
LCT 學習筆記
可能叫動態鏈剖分會比較合適?回顧重鏈剖分,我們將 x 所有兒子中子樹 siz 最大的那個兒子欽定為重兒子,乙個結點與它的重兒子的連邊稱為重邊,其餘稱為輕邊。重邊和輕邊形成的鏈把整棵樹剖分成了若干個 dfs 序上的連續段,這樣我們就能使用諸如線段樹等靜態資料結構維護鏈上的資訊了。重剖的侷限性在於它所維...
動態樹(LCT)學習筆記
candy flashhu xzyxzy 想要學lct的還是看這幾篇比較好。我這篇只是總結一些容易理解錯的或者一不小心打錯的地方。lct link cut tree 就是又可以link 動態加邊 又可以cut 動態刪邊 的維護一片森林的資料結構。lct使用實鏈剖分,對每一條實鏈用splay維護 一棵...