能夠按照<Utopiosphere>的調唱出來 「Link-Cut ,Time doesn’t stop .Prepare your doubts ,Eat them up」ios
參考資料:數據結構
1.popoqqq課件ide
2.《QTREE 解法的一些研究 》spa
3.http://blog.csdn.net/clove_unique/article/details/50991804.net
一【理論知識】code
Auxiliary Tree(輔助樹)blog
原樹與輔助樹的關係排序
輔助樹是不斷變化的,重鏈和輕鏈不斷變化get
二【實現】string
LCT用到的Splay和一般的仍是有點不一樣,沒有權值v,不進行查找操做,點編號就是原樹的編號
由於是一個Splay森林,多條重鏈多個根,因此用isRoot(x)判斷是否爲根,判斷isRoot(x)至關於判斷x的父親存不存在
rotate只是設置g的兒子時判斷isRoot(f)就好了
splay須要pushDown了(由於沒有kth了),也是判斷isRoot(pa)
Access和Cut更新了兒子關係,因此須要update
Access
實現:不斷把x splay到當前Atree的根,而後它的右子樹就是重兒子了,修改;用y輔助
注意:Access後x不必定爲這顆Splay的根,由於中途x變fa了
維護了節點信息別忘更新
MakeRoot
實現:Access後splay到根,而後全在x的左子樹上(權值是深度),區間翻轉便可
FindRoot
實現:MakeRoot後不斷往左找(不須要pushDown?加上也能夠啊。不加也對由於只是來判連通,判斷是否是在一棵原樹上,都不pushDown找到的仍是同一個點吧)
Link
實現:MakeRoot(x)而後t[x].fa=y
Cut
實現:MakeRoot(x)而後Access(y) splay(y) ,x就在y的左兒子了,t[y].ch[0]=t[x].fa=0;
維護了節點信息別忘更新
對x到y路徑上的點進行修改或查詢
只須要對x進行Move_To_Root操做,而後對y進行Access+Splay操做,那麼x到y路徑上的全部點都在以y爲根的子樹上
由於Access後x和y重鏈在一棵Splay上,x深度比y小
三【模板】
[update 2017-04-05]
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define lc t[x].ch[0] #define rc t[x].ch[1] #define pa t[x].fa typedef long long ll; const int N=3e5+5; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } namespace lct { struct meow{int ch[2], fa, rev, sum, w;} t[N]; inline int wh(int x) {return t[pa].ch[1] == x;} inline int isr(int x) {return t[pa].ch[0] != x && t[pa].ch[1] != x;} inline void update(int x) {t[x].sum = t[lc].sum ^ t[rc].sum ^ t[x].w;} inline void rever(int x) {t[x].rev ^= 1; swap(lc, rc);} inline void pushdn(int x) { if(t[x].rev) { if(lc) rever(lc); if(rc) rever(rc); t[x].rev = 0; } } void pd(int x) {if(!isr(x)) pd(pa); pushdn(x);} inline void rotate(int x) { int f=t[x].fa, g=t[f].fa, c=wh(x); if(!isr(f)) t[g].ch[wh(f)]=x; t[x].fa=g; t[f].ch[c] = t[x].ch[c^1]; t[t[f].ch[c]].fa=f; t[x].ch[c^1] = f; t[f].fa=x; update(f); update(x); } inline void splay(int x) { pd(x); for(; !isr(x); rotate(x)) if(!isr(pa)) rotate( wh(pa)==wh(x) ? pa : x ); } inline void access(int x) { for(int y=0; x; y=x, x=pa) splay(x), rc=y, update(x); } inline void maker(int x) { access(x); splay(x); rever(x); } inline int findr(int x) { access(x); splay(x); while(lc) pushdn(x), x=lc; return x; } inline void link(int x, int y) { maker(x); t[x].fa=y; } inline void cut(int x, int y) { maker(x); access(y); splay(y); t[x].fa = t[y].ch[0] = 0; update(y); } inline void split(int x, int y) { maker(x); access(y); splay(y); } } using lct::findr; int n, Q, op, x, y; int main() { freopen("in","r",stdin); n=read(); Q=read(); for(int i=1; i<=n; i++) lct::t[i].w = read(); for(int i=1; i<=Q; i++) { op=read(); x=read(); y=read(); if(op==0) lct::split(x, y), printf("%d\n", lct::t[y].sum); if(op==1) if(findr(x) != findr(y)) lct::link(x, y); if(op==2) if(findr(x) == findr(y)) lct::cut(x, y); if(op==3) lct::t[x].w = y, lct::splay(x); } }
四【一點好玩的東西】
1.LCT可作動態樹問題
2.LCT可作樹鏈剖分
3.LCT可作支持刪除邊的並查集(我太navie了.......並不能徹底實現這個功能,是一顆樹啊啊啊)
4.LCT可作不用排序的Kruskal(動態加邊的最小生成樹)