介绍
LCT是动态的树链剖分,他用
Splay
维护重链的信息,并且动态维护重链。
可以支持合并分割操作,并且动态维护树链的信息。
原树中的重链对应的是
Splay
中的实边,轻链对应轻边,这样一棵树被划分成了若干个
Splay
。
Splay
的中序遍历是从浅到深的顺序。
基本的操作
Splay-is_root()
bool is_root(){return pre->ch[0] != this && pre->ch[1] != this;}
//判断一个Splay节点是不是当前Splay的根。
Splay-rotate(x)
void rotate(node *now){
node *fa = now->pre, *gra = fa->pre;
int wh = now->wh();
//如果当前点的fa不是splay的根,那要跟新gra的孩子信息,这一句话要放在前面,后面fa的信息改变了,就判断不了了。
if(!fa->is_root()) gra->ch[gra->ch[0] == fa ? 0 : 1] = now;
fa->set_ch(wh, now->ch[wh^1]);
now->set_ch(wh^1, fa), now->pre = gra;
}
Splay-splay(x)
//需要先将当前点的所有的祖先节点的标记全部下放,在考虑操作当前点。
void splay(node *now){
int stt = 0; st[++ stt] = now;
for(node *i = now; !i->is_root(); i = i->pre) st[++ stt] = i->pre;
for(int i = stt; i >= 1; i --) st[i]->down();
//边界条件是now只要不是根就继续转,now->pre不是根,就考虑双旋。
for( ; !now->is_root(); rotate(now))
if(!now->pre->is_root())
now->wh() == now->pre->wh() ? rotate(now->pre) : rotate(now);
}
LCT-access(x)
//把x所在的树的重链,从根连接到x。
void access(node *x){
//最开始的一个
for(node *i = null; x != null; i = x, x = x->pre)
splay(x), x->set_ch(1, i);
}
LCT-makeroot(x)
//把x变成新的根,只需要把x到原根的路径全部取反即可。
void makeroot(node *x){
access(x), splay(x), x->rev ^= 1;
}
LCT-split(x, y)
//提取出x到y的一条链到一个splay里,并让x成为splay的根。
void split(node *x, node *y){
makeroot(y), access(x), splay(x);
}
LCT-link(x, y)
//将x变为根,直接向y连一条虚边即可。
void link(node *x, node *y){
makeroot(x); x->pre = y;
}
LCT-cut(x, y)
//切断x与y之间的边。
void cut(node *x, node *y){
//让x成为y的左子树,断开连接的边即可。
makeroot(x); access(y); splay(y);
y->set_ch(0, null); x->pre = null;
}
题目
BZOJ 2049 [Sdoi2008]洞穴勘测
思路:
LCT模板题,维护联通块即可。
代码:2049.cpp
BZOJ 2631 tree
思路:
LCT模板题,注意两个不同标记的合并就好了。