演算法初探 - Link-Cut Tree
更新記錄
【1】2020.10.18-16:34
- 1.完善內容
正文
如果給你一棵樹,並對這棵樹做兩種操作
- 改變某點的權值
- 詢問路徑上的異或和
這個時候我們可以用樹鏈剖分簡簡單單的秒掉這道題
那如果再加上兩種操作呢?
- 連線兩點
- 斷開兩點
這個時候就不能再用樹鏈剖分了,這種動態樹問題有一個專門的演算法:Link-Cut Trees
LCT維護的是一個森林!所以一定要分清原樹和輔助樹(Auxiliary Tree,這裡指一條重鏈上所有點構成的Splay)
毒瘤Tarjan為創始人之一
這個演算法依然要用到鏈剖分這個思想,但是用的不是重鏈剖分,而是實鏈剖分
為啥不用重鏈剖分呢
因為一連邊斷邊這棵樹的形態就改變
而實鏈剖分是可以動態變化的
剖分的思想很簡單:自己指定一條邊為實邊,剩下的就是虛邊了
我們規定用實邊連線的為實兒子,虛邊連線的為虛兒子
虛邊怎麼表示呢?
假設 f 為節點 n 的父節點
那麼從 n 可以訪問到 f
但是 f 無法訪問 n
這是因為有可能一個節點會有很多虛兒子,如果 f 能訪問 n Splay就不是二叉樹了
重要性質
每個Splay維護的路徑上的點在原樹中深度嚴格遞增,且這個Splay的中序遍歷得到的序列也是嚴格遞增
程式碼解釋
巨集定義
#define function(l,n) inline l n
#define R register int
變數
struct Node{
int f,son[2],v;
//父節點,子節點,值
bool re;
//翻轉標記
}t[N];
各種操作
nroot
not root的縮寫
判斷一個節點是否為所在的Splay的樹根
- 是的話返回false
- 不是的話返回true
function(bool,nroot)(int p) {return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
confirm
判斷此節點是父節點的左子節點還是右子節點
function(bool,confirm)(int p) {return t[t[p].f].son[1]==p;}
access
由於存在實虛邊,所以我們不能保證兩點在一棵Splay上
所以我們需要一種操作來使得兩點在一棵Splay上
假設我們access(x,y)
它的含義是以點x開始進行中序遍歷,到y結束,且遍歷得到的序列滿足上文所說的性質
我們不斷地去將一個點splay到當前所在的Splay的根
然後根據虛邊跳轉
之後根據LCT的性質重置子節點
以此類推,直到原樹根節點
function(void,access)(int p){
int son=0;
do{
splay(p);
t[p].son[1]=son;
pushup(p);
} while(p=t[son=p].f);
}
setroot
將一個點p設為LCT的根節點
我們想:如果只是splay的話,可能當前的節點與LCT的根節點不連通,所以我們先要access一下,之後splay
但此時這個點p為深度最大的點,也就是說它沒有右子樹
所以我們要reverse翻轉,此時p為深度最小的點,也就是根節點了
function(void,setroot)(int p) {access(p);splay(p);reverse(p);}
findroot
首先access一下,此時根節點一定是p所在的Splay的最小節點
我們將p旋轉到它所在的Splay的根,之後一路向左找就好
為了保證它的複雜度,我們最後還要再splay一次
function(int,findroot)(int p){
access(p);splay(p);
while(t[p].son[0]) pushdown(p),p=t[p].son[0];
splay(p);
return p;
}
link
setroot讓x點成為它所在的Splay的根,之後我們判斷一下連通性之後連一條虛邊即可
function(void,link)(int x,int y){
setroot(x);
if(findroot(y)!=x) t[x].f=y;
}
首先setroot
之後考慮什麼時候才能cut斷邊
- 兩點聯通
- 它們中序遍歷相鄰
中序遍歷相鄰意即它們為父子關係並且y沒有左子樹
cut
function(void,cut)(int x,int y){
setroot(x);
if(x==findroot(y)&&x==t[y].f&&!t[y].son[0]){
t[y].f=t[x].son[1]=0;
pushup(x);
}
}
split
獲取x - y的這條鏈,之後我們就可以方便的維護資訊了
function(void,split)(int x,int y) {setroot(x);access(y);splay(y);}
P3690 【模板】Link Cut Tree (動態樹)
#include<iostream>
#define function(l,n) inline l n
#define R register int
#define N 1000001
using namespace std;
int n,m,v[N],op,x,y,st[N];
struct Node{
int f,son[2],v;
bool re;
}t[N];
function(bool,nroot)(int p) {return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
function(bool,confirm)(int p) {return t[t[p].f].son[1]==p;}
function(void,pushup)(int p) {t[p].v=t[t[p].son[0]].v^t[t[p].son[1]].v^v[p];}
function(void,reverse)(int p) {swap(t[p].son[0],t[p].son[1]);t[p].re^=1;}
function(void,connect)(int up,int down,bool r) {t[up].son[r]=down;}
function(void,pushdown)(int p){
if(!t[p].re) return;
if(t[p].son[0]) reverse(t[p].son[0]);
if(t[p].son[1]) reverse(t[p].son[1]);
t[p].re=0;
}
function(void,rotate)(int p){
int fa=t[p].f,gfa=t[fa].f,np=confirm(p),ot=t[p].son[!np];
if(nroot(fa)) t[gfa].son[confirm(fa)]=p;
connect(p,fa,!np);
connect(fa,ot,np);
t[fa].f=p;t[p].f=gfa;
if(ot) t[ot].f=fa;
pushup(fa);
}
function(void,splay)(int p){
int fa=p,size=0,gfa;
st[++size]=fa;
while(nroot(fa)) st[++size]=fa=t[fa].f;
while(size) pushdown(st[size--]);
while(nroot(p)){
fa=t[p].f;gfa=t[fa].f;
if(nroot(fa)) rotate(confirm(p)==confirm(fa)?fa:p);
rotate(p);
}
pushup(p);
}
function(void,access)(int p){
int son=0;
do{
splay(p);
t[p].son[1]=son;
pushup(p);
} while(p=t[son=p].f);
}
function(void,setroot)(int p) {access(p);splay(p);reverse(p);}
function(int,findroot)(int p){
access(p);splay(p);
while(t[p].son[0]) pushdown(p),p=t[p].son[0];
splay(p);
return p;
}
function(void,link)(int x,int y){
setroot(x);
if(findroot(y)!=x) t[x].f=y;
}
function(void,cut)(int x,int y){
setroot(x);
if(x==findroot(y)&&x==t[y].f&&!t[y].son[0]){
t[y].f=t[x].son[1]=0;
pushup(x);
}
}
function(void,split)(int x,int y) {setroot(x);access(y);splay(y);}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(R i=1;i<=n;++i) cin>>v[i];
for(R i=1;i<=m;++i){
cin>>op>>x>>y;
if(!op){
split(x,y);cout<<t[y].v<<"\n";
} else if(op==1){
link(x,y);
} else if(op==2){
cut(x,y);
} else if(op==3){
splay(x);v[x]=y;
}
}
}