【模板】动态树(Link Cut Tree)
之前一直觉得lct是个特别高大上的东西,事实也是这样的,毕竟我也是写了差不多两天才把它写出来,当然算上肝Splay的时间。
lct本质上是一种奇怪的树链剖分,名叫实链剖分,这也是我继重链剖分和长链剖分之后学习的第三种树链剖分。树链剖分的目的都是想要把树上问题转化成链上问题从而降低题目难度,只不过划分方式有别。
lct这个实链剖分似乎并没有什么规则可言,所以它的复杂度也是如此捉摸不透,当然它有复杂度证明的论文只不过我看不懂。它是考虑对于每一条实链维护一棵平衡树,多数情况下是Splay,那么序列询问就变成了对于某个Splay的询问。至于加边,由于不同实链的Splay之间只靠根节点父亲有微弱的联系,在数值上并木有什么关系,于是就可以考虑makeroot之后把根暴力连接到另一个点即可。至于删边,可以考虑利用Splay强大的分裂属性,把一个点作为根另一个点access上去,那么会形成一个二人世界的Splay,把中间那条边删去即可。特判和单点修改什么的就不多说了,区间修改应该大同小异吧,只不过lazy和pushdown可能会复杂一点。
分函数说一下:
inline bool check(int x){
int fa=t[x].f;
return x==t[fa].ch[0]||x==t[fa].ch[1];
}//判断一个点是否不是根节点
inline void rotate(int x){
int y=t[x].f;int z=t[y].f,kk=t[y].ch[1]==x;
if(check(y))t[z].ch[t[z].ch[1]==y]=x;
if(t[x].ch[!kk])t[t[x].ch[!kk]].f=y;
//2+4模式,z的孩子和x孩子的父亲要特判,其它该怎么就怎么
t[x].f=z;t[y].f=x;
t[y].ch[kk]=t[x].ch[!kk];
t[x].ch[!kk]=y;
pushup(y);pushup(x);
}
//由于lct中splay可能是突兀地选择一个点(而不是自根访问到这个点),所以需要高级pushdown
inline void pushdd(int x){
if(check(x))pushdd(t[x].f);
pushdown(x);
}//将路径上所有点pushdown
inline void splay(int x){
pushdd(x);
while(check(x)){//只是判断条件改了下
int y=t[x].f;int z=t[y].f;
if(check(y))(t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
rotate(x);
}
}
//灵魂,access函数
inline void access(int x){
//记录的是:x为稍高的那个点,y是曾经的那个点
//把x-y变成实链相当于是splay(x)之后舍弃x的右子树(它们深度都大于x),把右儿子的位置让给y
for(int y=0;x;x=t[y=x].f){
splay(x);rc=y;
if(y)t[y].f=x;
pushup(x);
}
}
//两个比较奇怪的函数
inline void makeroot(int x){
access(x);
splay(x);//很明显此时root和x是Splay中深度最大的和最小的两个点
t[x].lazy^=1;//可以想到把x置为新根之后,原来离根近的点现在离根远,所有点的深度都会反着来,打lazy标记
//但对于这棵原树以外的Splay来说,原来离根远的点仍然离根远,大不了大家离根距离都加上一个值,大小关系无所谓啊
}
inline int findroot(int x){
access(x);splay(x);//相当于是在x为根的Splay中找深度最小的点
while(rc)pushdown(x),x=rc;//由于中序遍历深度递增,越右边的点深度越小
return x;
}
其它便没有什么了。其它的一些操作,基本上都是对于上面函数的调用。
代码:
#include<cstdio>
#define zczc
const int N=100010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,n;
#define lc t[x].ch[0]
#define rc t[x].ch[1]
struct node{
int ch[2],f,data,an;
bool lazy;
}t[N];
inline void swap(int &s1,int &s2){
int s3=s1;s1=s2;s2=s3;return;
}
inline void pushup(int x){
t[x].an=t[lc].an^t[rc].an^t[x].data;
}
inline bool check(int x){
int fa=t[x].f;
return x==t[fa].ch[0]||x==t[fa].ch[1];
}
inline void rotate(int x){
int y=t[x].f;int z=t[y].f,kk=t[y].ch[1]==x;
if(check(y))t[z].ch[t[z].ch[1]==y]=x;
if(t[x].ch[!kk])t[t[x].ch[!kk]].f=y;
t[x].f=z;t[y].f=x;
t[y].ch[kk]=t[x].ch[!kk];
t[x].ch[!kk]=y;
pushup(y);pushup(x);
}
inline void pushdown(int x){
if(!t[x].lazy)return;
swap(lc,rc);
t[x].lazy=false;
t[lc].lazy^=1,t[rc].lazy^=1;
}
inline void pushdd(int x){
if(check(x))pushdd(t[x].f);
pushdown(x);
}
inline void splay(int x){
pushdd(x);
while(check(x)){
int y=t[x].f;int z=t[y].f;
if(check(y))(t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
rotate(x);
}
}
inline void access(int x){
for(int y=0;x;x=t[y=x].f){
splay(x);rc=y;
if(y)t[y].f=x;
pushup(x);
}
}
inline void makeroot(int x){
access(x);
splay(x);
t[x].lazy^=1;
}
inline int findroot(int x){
access(x);splay(x);
while(rc)pushdown(x),x=rc;
return x;
}
inline void work(int x,int y){
makeroot(x);access(y);splay(y);
printf("%d\n",t[y].an);
}
inline void makelink(int x,int y){
makeroot(x);
if(findroot(y)^x)t[x].f=y;
}
inline void cut(int x,int y){
makeroot(x);access(y);splay(y);
if(t[y].ch[0]!=x)return;
t[y].ch[0]=0;t[x].f=0;pushup(y);
}
inline void change(int x,int val){
splay(x);t[x].data=val;
pushup(x);
}
inline void print(){
printf("___________\n");
for(int i=1;i<=m;i++)
printf("%d %d %d:%d&%d\n",i,t[i].data,t[i].f,t[i].ch[0],t[i].ch[1]);
printf("___________\n");
}
#undef lc
#undef rc
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);read(n);
for(int i=1;i<=m;i++){read(t[i].data);t[i].an=t[i].data;}
int op,s1,s2;
while(n--){
read(op);read(s1);read(s2);
switch(op){
case 0:work(s1,s2);break;
case 1:makelink(s1,s2);break;
case 2:cut(s1,s2);break;
case 3:change(s1,s2);break;
}
}
return 0;
}
一如既往,万事胜意