傳聞:《狡狐大冒險5》今年下半年公佈
因為博主很懶,所以這裡只是簡單介紹 LCT 的原理,並不會配圖講解,主要是為了幫助博主日後理解 LCT,所以初學者請另找部落格觀看。
LCT 有一種樹鏈剖分,分為兩種鏈,實鏈和虛鏈,注意這個鏈是實是虛使我們認為規定的,也就是說我們從每個節點的向下連邊中選出一條邊來變成實邊,其餘邊為虛邊。注意在實際操作中,可能有的節點向下連邊全是虛邊,不過這種情況僅當這個節點向下連邊只有一條時才能夠出現,
我們考慮用 LCT 來維護一個森林,我們支援在兩個節點之間連邊,詢問鏈資訊,斷掉兩個節點之間的邊。
我們引入輔助樹,對每條實鏈,我們都用一個 Splay 去維護,這顆 Splay 中序遍歷後是這條實鏈按照深度從小到大,我們考慮虛邊就是若干 Splay 之間的的邊,我們這樣來連線,設原來有一條虛邊為 \((x,y)\)
我們首先可以有每個 Splay 中的 rotate 和 Splay 操作,這個好實現,只需要注意我們不要把輕邊父親那過來左旋,注意這一點只需要維護一個是否為根的函式,如果一個節點為根,那麼它的父親不會認它。
我們還可以實現把一個節點通過改變邊的實虛來把一個節點連到根節點,具體來說,我們把當前點弄到根上,然後斷掉其右兒子,找到父親,把其右兒子設為當前點,不斷重複這樣的操作。
容易觀察到,這樣做完之後,這條鏈上深度最大的點就是我們操作的這個點,所以我們同樣可以利用這個操作來剖路徑。
我們還可以指定一個節點為根,這樣配合上面的操作,就可以進行剖路徑。具體來說,我們把這個節點和當前根相連,容易發現,我們連完之後操作的最後一個點就是根,如果我們旋轉其所有的左右兒子,深度最大的點就會變成深度最小的點,這樣整棵樹的根就變成了我們所需要的。
我們可以剖一個路徑出來變成一個 Splay,這樣路徑操作就可以通過打標機來完成。
Link 和 Cut 操作,首先我們可以實現找到當前樹的根,這個通過上面的操作是很好實現的。
Link 操作,只需要把一個弄為當前樹的根和 Splay 的根之後直接像另一個點連虛邊,這些 Splay 對虛邊的條數是沒有限制的。
Cut 操作,只需要判斷邊是否存在後直接割掉邊就可以,注意 PushUp。
執行 Find 操作,也就是找根最後一定要記住 Splay 根節點,目的是為了保證複雜度。
#include<bits/stdc++.h>
#define mset(a,b) memset((a),(b),sizeof((a)))
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define dec(i,l,r) for(int i=(r);i>=(l);i--)
#define inc(a,b) (((a)+(b))>=mod?(a)+(b)-mod:(a)+(b))
#define sub(a,b) (((a)-(b))<0?(a)-(b)+mod:(a)-(b))
#define mul(a,b) 1ll*(a)*(b)%mod
#define sgn(a) (((a)&1)?(mod-1):1)
#define cmax(a,b) (((a)<(b))?(a=b):(a))
#define cmin(a,b) (((a)>(b))?(a=b):(a))
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define N 100010
#define M number
using namespace std;
typedef double dd;
typedef long double ld;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
//#define int long long
typedef pair<int,int> P;
typedef vector<int> vi;
const int INF=0x3f3f3f3f;
const dd eps=1e-9;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int tag[N],val[N],ch[N][2],fa[N],n,m,a[N];
inline void Print(){
rep(i,1,n){
printf("k=%d tag=%d val=%d ch[0]=%d ch[1]=%d fa=%d\n",i,tag[i],val[i],ch[i][0],ch[i][1],fa[i]);
}
}
#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
struct LCT{
inline int Get(int k){return rs(fa[k])==k;}
inline void PushUp(int k){
val[k]=a[k]^val[ls(k)]^val[rs(k)];
}
inline void C(int k){
swap(ls(k),rs(k));tag[k]^=1;
}
inline void PushDown(int k){
if(tag[k]){
C(ls(k));C(rs(k));tag[k]^=1;;
}
}
inline bool Root(int k){
int fat=fa[k];
return ls(fat)!=k&&rs(fat)!=k;
}
inline void rotate(int k){
int y=fa[k];int z=fa[y],x=Get(k);
if(!Root(y)) ch[z][Get(y)]=k;
ch[y][x]=ch[k][x^1];fa[ch[k][x^1]]=y;
ch[k][x^1]=y;fa[y]=k;fa[k]=z;
PushUp(y);PushUp(k);
}
inline void Update(int k){
if(!Root(k)) Update(fa[k]);
PushDown(k);
}
inline void Splay(int k){
Update(k);
for(int f=fa[k];!Root(k);rotate(k),f=fa[k]){
if(!Root(f)) rotate(Get(k)==Get(f)?f:k);
}
}
inline int Access(int k){
int p;
for(p=0;k;p=k,k=fa[k]){
Splay(k);rs(k)=p;PushUp(k);
}
return p;
}
inline void MakeRoot(int k){
k=Access(k);C(k);
}
inline void Split(int x,int y){
MakeRoot(x);Access(y);Splay(y);
}
inline int Find(int k){
Access(k);Splay(k);PushDown(k);
while(ls(k)) k=ls(k),PushDown(k);
Splay(k);return k;
}
inline void Cut(int a,int b){
MakeRoot(a);
if(Find(b)==a&&rs(a)==b&&ls(b)==0){
rs(a)=fa[b]=0;PushUp(a);
}
}
inline void Link(int a,int b){
MakeRoot(a);if(Find(b)==a) return;Splay(a);fa[a]=b;
}
inline void Change(int k,int x){
MakeRoot(k);a[k]=x;PushUp(k);
}
inline int Ask(int a,int b){
Split(a,b);return val[b];
}
}lct;
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(m);
rep(i,1,n){
read(val[i]);a[i]=val[i];
}
rep(i,1,m){
int op,x,y;read(op);read(x);read(y);
if(op==0){
printf("%d\n",lct.Ask(x,y));
}
else if(op==1){
lct.Link(x,y);
}
else if(op==2){
lct.Cut(x,y);
}
else if(op==3){
lct.Change(x,y);
}
}
return 0;
}