算法初探 - 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;
}
}
}