前言
和上次的splay一样,把你们的Independence
开起来,今天我们再嗨一次!
学习Link Cut Tree需要一定的splay的基础,如果你的基础还是像我一样不够扎实,请点击上面的链接,学习一下
splay
s
p
l
a
y
再走.
这个博客真是命运多舛.写完了突然卡了发布不了,然后刷新一下又变回原来这样子了.
背景
想必大家都知道LCT指的是动态树了.
原理
首先它到底是维护什么东西的呢?
大家肯定是学过树链剖分的.
树链剖分是将树按照重儿子剖成一条一条的链,也被称为重链剖分.
而LCT是实链剖分.
所谓实链剖分
,是对于每一个节点将它和它某一个儿子的边划为实边,和其他儿子的边都划为虚边.
而虚实边是在不断地变化之中的,因此不能使用线段树来维护,而应该使用splay.
LCT维护的对象是一个森林,借助实链剖分,它可以支持的操作多到无法想象.
它有几条性质:
1.每一个splay是维护一条链,链上不能有两个深度相同的点,并且中序遍历每一棵splay得到的点在原树中的深度是严格递增的.
2.每一个节点仅仅包含于一棵splay中.
3.实边包含在splay中,虚边从一棵splay指向另一个节点(该splay中中序遍历最靠前的节点在原树中的父亲.)
注意被虚边指向的点不能沿着虚边访问回去,即认父不认子.
操作
access
Link Cut Tree 中最重要的一个操作,是打通某节点到根的路径,也即将该节点与根放进同一棵splay.
明显此时在原树中从根节点到该节点的边全部变为实边,与路径上的点无关的其它点之间的边相对变为虚边.
那么操作顺序是这样的.
1.设当前被操作点为x,则先把x splay到当前splay的根.
2.把x的右儿子设为y.
3.更新x的信息.
4.让y=x,将x变为x虚边指向的父亲.
所以代码如下.只需要一个for循环.
void access(int x){
for (int y=0;x;y=x,x=fa[x])
splay(x),rs(x)=y,push_up(x);
}
make_root
仅仅是将根和某个点之间的路径拉出来并没有什么用处.现在我们需要两个节点之间的路径的信息.,
然而发生路径不能满足按照深度严格递增的情况,由于性质1,这样的路径不能产生在一个splay中.
这个时候我们要利用access
和splay
两个操作.
先把
x
x
access
一下,然后splay
到根.
这个时候思考一下人生.
此时的是splay里面深度最大的点,没有右子树.我们只要把整棵树颠倒过来,就可以使
x
x
没有左子树,从而变成深度最小的点,也就是根.
int rev(int x){
swap(ls(x),rs(x)),tag[x]^=1;//交换左右儿子并打标记
}
void mkrt(int x){
access(x),splay(x),rev(x);
}
find_root
可以找到所代表原树的树根,用来判断两点间的连通性.
先把
x
x
access
一下,再splay
到根,不断地找它的左儿子,就会找到深度最小的节点,也就是根.
void push_down(int x){
if (tag[x]) rev(ls(x)),rev(rs(x)),tag[x]=0;
}
int fdrt(int x){
access(x),splay(x);
for (;ls(x);x=ls(x)) push_down(x);//注意push_down
return x;
}
split
现在我们可以make_root
,我们可以利用上面的工具直接访问两点在树上的路径.
void split(int x,int y){
mkrt(x),access(y),splay(y);
}
//此时直接调取y的信息就可以了.
link
连一条
x→y
x
→
y
的边.
把
x
x
变成之后,判断
y
y
和是否已经连接.
如果find_root(y)=x
说明
x,y
x
,
y
已经连接,直接返回.
int link(int x,int y){
mkrt(x);
return fdrt(y)^x?fa[x]=y,1:0;
}
cut
断开
x,y
x
,
y
之间的边.
如何判断
x,y
x
,
y
之间是否已经连边?
首先它们必须要直接或者间接连通.
所以find(y)=x
.
如果
y
y
与没有直接的父子关系,必然能找到一些点,它的中序遍历在
x,y
x
,
y
之间.
所以fa[x]=y.
然而即使
y
y
是的父亲,
x
x
也不能有右子树.
若存在右子树,它右子树中的点的中序遍历也是在
x,y
x
,
y
之间的.
所以rs(x)=0
.
接下来将fa[x]
和ls(y)
清空.显然
x
x
一定是的左儿子.
int cut(int x,int y){
mkrt(x);
if (fdrt(y)!=x||fa[x]^y||rs(x)) return 0;
return fa[x]=ls(y)=0,1;
}
代码实现
#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
int x=0,f=1;char c=gc();
for (;!isdigit(c);c=gc()) f^=c=='-';
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return f?x:-x;
}
template <typename mitsuha>
inline bool read(mitsuha &x){
x=0;int f=1;char c=gc();
for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
if (!~c) return 0;
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return x=f?x:-x,1;
}
template <typename mitsuha>
inline int write(mitsuha x){
if (!x) return 0&pc(48);
if (x<0) x=-x,pc('-');
int bit[20],i,p=0;
for (;x;x/=10) bit[++p]=x%10;
for (i=p;i;--i) pc(bit[i]+48);
return 0;
}
inline char fuhao(){
char c=gc();
for (;isspace(c);c=gc());
return c;
}
}using namespace chtholly;
using namespace std;
const int yuzu=3e5;
typedef int fuko[yuzu|10];
int n,m;
struct link_cut_tree{
fuko fa,ch[2],tag,sum,val;
#define ls(x) ch[0][x]
#define rs(x) ch[1][x]
#define ws(x,y) (rs(x)==y)
int nrt(int x){return ls(fa[x])==x||rs(fa[x])==x;}// not_root
int rev(int x){swap(ls(x),rs(x)),tag[x]^=1;}
void push_down(int x){if (tag[x]) rev(ls(x)),rev(rs(x)),tag[x]=0;}
void push_up(int x){sum[x]=sum[ls(x)]^sum[rs(x)]^val[x];}
void zhuan(int x){
int y=fa[x],z=fa[y],k=ws(y,x),ps=ch[!k][x];
if (nrt(y)) ch[ws(z,y)][z]=x;
ch[!k][x]=y,ch[k][y]=ps;
if (ps) fa[ps]=y;
fa[y]=x,fa[x]=z,push_up(y);
}
void pushall(int x){
if (nrt(x)) pushall(fa[x]);
push_down(x);
}
int splay(int x){
pushall(x);
for (;nrt(x);zhuan(x)){
int y=fa[x],z=fa[y];
if (nrt(y)) zhuan(ws(y,x)^ws(z,y)?x:y);
}push_up(x);
}
int access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs(x)=y,push_up(x);}
void mkrt(int x){access(x),splay(x),rev(x);}
void split(int x,int y){mkrt(x),access(y),splay(y);}
int fdrt(int x){
access(x),splay(x);
for (;ls(x);x=ls(x)) push_down(x);
return x;
}
int link(int x,int y){
mkrt(x);
return fdrt(y)^x?fa[x]=y,1:0;
}
int cut(int x,int y){
mkrt(x);
if (fdrt(y)!=x||(fa[x]^y)||rs(x)) return 0;
return fa[x]=ls(y)=0,push_up(y),1;
}
}my_;
#define split my_.split
#define link my_.link
#define cut my_.cut
#define val my_.val
#define splay my_.splay
#define sum my_.sum
int main(){
n=read(),m=read();
for (int i=1;i<=n;++i) val[i]=read();
for (;m--;){
int op=read(),x=read(),y=read();
switch(op){
case 0: split(x,y),write(sum[y]),pl; break;
/*把x,y开到同一个splay里,直接取y的值就可以了.*/
case 1: link(x,y); break;
case 2: cut(x,y); break;
case 3: splay(x),val[x]=y; break;
/*先把x转到根,然后再改变val,不会影响父亲的值.*/
}
}
}
谢谢大家.