LUOGU P3690 【模板】Link Cut Tree (lct)
阿新 • • 發佈:2018-11-21
解題思路
\(lct\)就是基於實鏈剖分,用\(splay\)來維護每一條實鏈,\(lct\)的維護物件是一棵森林。\(lct\)支援很多神奇的操作:
\(1、\) \(access\):這是\(lct\)的核心操作,就是將一個點與根打通,就是把路徑上的所有邊變成實邊,具體就是轉到根,換兒子,更新資訊。
\(2、\)\(makeroot\):將指定點作為原樹的根,就是先\(access\)後,\(x\)一定是深度最大的點,再\(splay\) \(x\) 後將\(x\)的兒子進行翻轉,這樣就可以使\(x\)變為深度最小的節點,即為根。
\(3、\)\(findroot\)
\(4、\)\(split\) :訪問\(x\)到\(y\)這條鏈,就是先\(makeroot\) \(x\),讓\(x\)變為根,再\(access\) \(y\)把\(y\)與\(x\)打通,最後把\(y\)轉上去就行了。
\(5、\)\(link\):連邊,就是把其中一個點\(makeroot\),然後直接連一條虛邊。
\(6、\) \(cut\):斷邊,先把\(x\)與\(y\)的路徑拿出來,就是\(split\)
剩下的一些操作跟\(splay\)比較像。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; const int MAXN = 300005; inline int rd(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,w[MAXN]; int fa[MAXN],sum[MAXN],ch[MAXN][2]; bool tag[MAXN]; inline bool check(int x){ return (x==ch[fa[x]][1]); } inline bool isroot(int x){ return (ch[fa[x]][0]!=x && ch[fa[x]][1]!=x); } inline void pushup(int x){ sum[x]=sum[ch[x][0]]^sum[ch[x][1]]^w[x]; } inline void pushdown(int x){ if(tag[x]){ if(ch[x][0]) tag[ch[x][0]]^=1,swap(ch[ch[x][0]][0],ch[ch[x][0]][1]); if(ch[x][1]) tag[ch[x][1]]^=1,swap(ch[ch[x][1]][0],ch[ch[x][1]][1]); tag[x]=0; } } inline void update(int x){ if(!isroot(x)) update(fa[x]);pushdown(x); } inline void rotate(int x){ int y=fa[x],z=fa[y];bool chk=check(x); if(!isroot(y)) ch[z][(y==ch[z][1])]=x; ch[y][chk]=ch[x][chk^1];fa[ch[x][chk^1]]=y; fa[y]=x;fa[x]=z;ch[x][chk^1]=y; pushup(y);pushup(x); } inline void splay(int x){ update(x); for(;!isroot(x);rotate(x)) if(!isroot(fa[x])) rotate(check(x)==check(fa[x])?fa[x]:x); } inline void access(int x){ for(int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y,pushup(x); } inline void makeroot(int x){ access(x);splay(x); tag[x]^=1;swap(ch[x][0],ch[x][1]); } inline int findroot(int x){ access(x);splay(x); while(ch[x][0]) pushdown(x),x=ch[x][0]; return x; } inline void split(int x,int y){ makeroot(x);access(y);splay(y); } inline void link(int x,int y){ if(findroot(x)==findroot(y)) return; makeroot(x);fa[x]=y; } inline void cut(int x,int y){ if(findroot(x)!=findroot(y)) return; split(x,y);if(ch[y][0]==x && !ch[x][1]) fa[x]=0,ch[y][0]=0,pushup(y); } int main(){ n=rd(),m=rd();int op,x,y; for(int i=1;i<=n;i++) w[i]=rd(); while(m--){ op=rd(),x=rd(),y=rd(); if(op==0) {split(x,y);printf("%d\n",sum[y]);} if(op==1) link(x,y); if(op==2) cut(x,y); if(op==3) splay(x),w[x]=y; } return 0; }