LCT動態樹入門
LCT,link-cut-tree,一種基於splay的高級數據結構,常用於維護動態森林問題,但ta只能維護子樹信息,無法修改子樹信息。
首先,如果你不會splay,來這裏看看吧。
接下來步入正題。
首先闡述一下個人對LCT的理解,其實你可以把LCT理解成許多棵splay,每一個聯通塊是一棵大splay,每個大splay中有許多的小splay,小splay的根靠虛邊連上大splay,虛邊是什麽?即假設你從x向y連一條虛邊,便只要將x父親設為y,不用將y的兒子設為x。
但每一棵小splay中的邊都是正常的樹邊。那麽,判斷一個點是不是splay的根,就要這樣
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
那麽,每棵小splay中的到底什麽?你可以將其理解為你查詢的歷史路徑。因為每當你要統計一條路徑上的信息,就要用到access操作
inline void access(int x)
{
for(register int y=0;x;y=x,x=fa[x])
{
splay(x);ch[x][1]=y;push_up(x);
}
}
access看英文就知道,這個函數相當於打通了一條從該節點到root的通道,每次將這個點旋到當前splay的root,再將以前那一棵的root接到這一棵的右兒子上,就這樣一路旋上去,最後,這個點會停在主splay的最下面。
inline void makeroot(int x)
{
access(x);splay(x);rev[x]^=1;
}
makeroot,顧名思義,把這個節點作為根,為什麽要打翻轉標記?因為LCT維護每個點的相對深度,當你把一個點旋到root,例如是一條鏈,你把點從最右邊旋上來,那麽本來這個點應該是最深的,現在變成最淺的了,所以打上翻轉標記,使他依然是最深的。
當你要統計x到y路徑上的某些信息是,只需要makeroot(x),access(y),這時,x到y中間的點就是你要的路徑,再splay(y),y旋上去時push_up就會把路徑上的信息統計掉,所以ans就是y點信息了。
int l[N];
inline void splay(int x)
{
l[0]=0;
int y=x;
while(1)
{
l[++l[0]]=y;
if(isroot(y))break;
y=fa[y];
}
Fordown(i,l[0],1)push_down(l[i]);
while(!isroot(x))
{
//if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
rotate(x);
}
}
這裏的splay,因為有翻轉操作,所以要先把路徑上所有點入隊,從上面開始依次下放翻轉標記
inline int find(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
return x;
}
find操作,其實就是找到左邊的點,用來判斷兩個點在不在一個聯通塊中
inline void link(int x,int y)
{
if(find(x)==find(y))return;
makeroot(x);
fa[x]=y;
}
inline void cut(int x,int y)
{
makeroot(x);
access(y);splay(y);
if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
}
如果你弄清楚了前面幾個操作,那麽link和cut操作就很簡單了
最後,在rotate的時候,如果你需要維護路徑上的信息就需要push_up,但因為題目的不同而oush_up的東西不一樣,所以要根據題目來定,這裏就拿維護路徑上的^值,所以這樣寫
inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
下面是例題
洛谷LCT模板(3690)要就維護路徑^值
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=3e5+5;
bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
T ans=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!=‘-‘)ch=getchar();
if(ch==‘-‘)f=-1,ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-‘0‘),ch=getchar();
return ans*f;
}
void file()
{
#ifndef ONLINE_JUDGE
freopen("LCT.in","r",stdin);
freopen("LCT.out","w",stdout);
#endif
}
int n,m,val[N],sum[N];
int ch[N][2],fa[N],rev[N];
inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
inline void push_down(int x)
{
if(rev[x])
{
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline bool get(int x){return x==ch[fa[x]][1];}
inline void rotate(int x)
{
int old=fa[x],oldfa=fa[old],o=get(x);
if(!isroot(old))ch[oldfa][get(old)]=x;
fa[x]=oldfa;fa[ch[x][o^1]]=old;fa[old]=x;
ch[old][o]=ch[x][o^1];ch[x][o^1]=old;
push_up(old);push_up(x);
}
int l[N];
inline void splay(int x)
{
l[0]=0;
int y=x;
while(1)
{
l[++l[0]]=y;
if(isroot(y))break;
y=fa[y];
}
Fordown(i,l[0],1)push_down(l[i]);
while(!isroot(x))
{
if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
rotate(x);
}
}
inline void access(int x)
{
for(register int y=0;x;y=x,x=fa[x])
{
splay(x);ch[x][1]=y;push_up(x);
}
}
inline void makeroot(int x)
{
access(x);splay(x);rev[x]^=1;
}
inline int find(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
return x;
}
inline void link(int x,int y)
{
if(find(x)==find(y))return;
makeroot(x);
fa[x]=y;
}
inline void cut(int x,int y)
{
makeroot(x);
access(y);splay(y);
if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
}
void work()
{
while(m--)
{
static int opt,x,y;
opt=read<int>();x=read<int>();y=read<int>();
if(opt==0)
{
makeroot(x);
access(y);splay(y);
printf("%d\n",sum[y]);
}
else if(opt==1)link(x,y);
else if(opt==2)cut(x,y);
else if(opt==3)
{
access(x);splay(x);
val[x]=y;push_up(x);
}
}
}
inline void input()
{
n=read<int>();m=read<int>();
For(i,1,n)sum[i]=val[i]=read<int>();
}
int main()
{
file();
input();
work();
return 0;
}
LCT動態樹入門