• LCT小结


    前言

    去年其实已经学过(LCT)了 ,但因为准备(noip)就没做什么题,忘得差不多啦,来补份总结

    其实(LCT)是可以用(FHQ\_treap)实现的,但似乎更慢??某(dalao)测试过的

    反正(LCT)码量小(稍微压点就(60)行以下了),而且网上大部分(LCT)都是用(splay)操作的,暂时不建议大家用(FHQ\_treap)

    注:本文过于简短,只适合做总结观看

    性质

    (LCT),全称(Link Cut Tree),是一种维护动态树的利器

    (1.)每个原树节点存在且仅存在于一棵(splay)

    (2.)整个(LCT),是由多棵(splay)构成的森林,其中每棵(splay)维护的是一条从上至下在原树中深度递增的路径,中序遍历后的点序列的在原树中的深度递增

    (3.LCT)里的点靠实边和虚边连接,实边在(splay)

    虚边是由一棵(splay)指向另一棵(splay)的某个节点:该(splay)中的根指向原树深度最小的点的父亲(根与深度最小的点不等价)

    当原树某点有多个儿子时,其中一个儿子存在于同棵(splay)且拉实边,其他儿子存在于其他的(splay)向该点拉虚边

    (Update(x))更新(x)某个要维护的值

    inline void Update(LL x){
    	sum[x]=val[x]^sum[son[x][0]]^sum[son[x][1]];
    }
    

    (Notroot(x)()不该棵(splay)为根()Splay(x)()上旋到该棵(splay)的根(),Rotate(x))

    这三个函数不建议大家改动,老老实实打就好了,容易出错 且与标准(splay)有部分差别

    inline bool Notroot(LL x){
    	return son[fa[x]][0]==x||son[fa[x]][1]==x;
    }
    inline void Rotate(LL x){
    	LL y=fa[x],z=fa[y],lz=(son[y][1]==x);;
    	if(Notroot(y)){
    		son[z][son[z][1]==y]=x;//**
    	}
    	son[y][lz]=son[x][lz^1],fa[son[y][lz]]=y;
    	son[x][lz^1]=y,fa[y]=x;
    	fa[x]=z;
    	Update(y),Update(x);
    }
    inline void Splay(LL x){
    	LL y(x),top(0);
    	sta[++top]=y;//**
    	while(Notroot(y)) sta[++top]=y=fa[y];//**
    	while(top) Pushdown(sta[top--]);//**
    	while(Notroot(x)){
    		y=fa[x];
    		if(Notroot(y)){
    			LL z(fa[y]);
    			if(((son[y][0]==x)^(son[z][0]==y))==0) Rotate(y);
    			else Rotate(x);
    		}
    		Rotate(x);
    	}
    }
    

    (Access(x))拉链1(维护(x)到原树根)

    每次把节点(x)上旋到根,因为我们是要维护这一条链,当然比(x)深度更大的点(右子树)丢到另外的(splay)里去

    细节:并不等效于拉一条以实根为根的链,故查询链时需注意直接(Split(x,y))再查询(y)

    inline void Access(LL x){
    	for(LL y=0;x;y=x,x=fa[x]){
    		Splay(x),son[x][1]=y,Update(x);
    	}
    }
    

    比如我们(Access(N)),看一下是怎么具体移动的


    (Makeroot(x))(换根)

    通常我们维护的((x,y))是位于原根的两个不同的子树,可是(Access(x))只能维护(x)到原根这条链

    一棵树随便提一点上来都还是树,那我们维护引入换根操作通过重构(LCT),使得满足原有性质上换原根

    比如我们维护((x,y))这条链,就(Makeroot(x),Access(y))

    来看看(Makeroot(x))的具体代码:当我们把(x)上旋到根时是没有右子树的((x)深度最大),而我们既然要换根得让(x)变成深度最小的,就交换左右子树

    inline void Pushr(LL x){
    	swap(son[x][0],son[x][1]);r[x]^=1;
    }
    inline void Makeroot(LL x){
    	Access(x),Splay(x),Pushr(x);
    }
    

    (Pushdown(x))下传标记

    inline void Pushdown(LL x){
    	if(r[x]){
    		if(son[x][0]){Pushr(son[x][0]);}
    		if(son[x][1]){Pushr(son[x][1]);}
    		r[x]=0;
    	}
    }
    

    (Findroot(x))找根

    inline LL Findroot(LL x){
    	Access(x),Splay(x);
    	while(son[x][0]){
    		Pushdown(x),x=son[x][0];
    	}Splay(x);
    	return x;
    }
    

    (Split(x,y))拉链2(维护(x)(y))

    inline void Split(LL x,LL y){
    	Makeroot(x),Access(y),Splay(y);
    }
    

    (Link(x,y))动态连边

    inline void Link(LL x,LL y){
    	Makeroot(x);
    	if(Findroot(y)!=x) fa[x]=y;
    }
    

    (Cut(x,y))动态删边

    inline void Delet(LL x,LL y){
    	Split(x,y);
    	if(son[y][0]!=x||son[x][1]) return;//注意这个地方
    	fa[x]=son[y][0]=0;
    	Update(y);
    }
    

    模板题

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<stack>
    using namespace std;
    typedef int LL;
    const LL maxn=1e6;
    inline LL Read(){
    	LL x(0),f(1);char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    	return x*f;
    }
    LL n,m;
    LL fa[maxn],son[maxn][2],val[maxn],sum[maxn],r[maxn];
    
    inline bool Notroot(LL x){
    	return son[fa[x]][0]==x||son[fa[x]][1]==x;
    }
    inline void Pushr(LL x){
    	swap(son[x][0],son[x][1]);r[x]^=1;
    }
    inline void Pushdown(LL x){
    	if(r[x]){
    		if(son[x][0]){Pushr(son[x][0]);}
    		if(son[x][1]){Pushr(son[x][1]);}
    		r[x]=0;
    	}
    }
    inline void Update(LL x){
    	sum[x]=val[x]^sum[son[x][0]]^sum[son[x][1]];
    }
    inline void Rotate(LL x){
    	LL y=fa[x],z=fa[y],lz=(son[y][1]==x);;
    	if(Notroot(y)){
    		son[z][son[z][1]==y]=x;//**
    	}
    	son[y][lz]=son[x][lz^1],fa[son[y][lz]]=y;
    	son[x][lz^1]=y,fa[y]=x;
    	fa[x]=z;
    	Update(y),Update(x);
    }
    LL sta[maxn];
    inline void Splay(LL x){
    	LL y(x),top(0);
    	sta[++top]=y;//**
    	while(Notroot(y)) sta[++top]=y=fa[y];//**
    	while(top) Pushdown(sta[top--]);//**
    	while(Notroot(x)){
    		y=fa[x];
    		if(Notroot(y)){
    			LL z(fa[y]);
    			if(((son[y][0]==x)^(son[z][0]==y))==0) Rotate(y);
    			else Rotate(x);
    		}
    		Rotate(x);
    	}
    }
    inline void Access(LL x){
    	for(LL y=0;x;y=x,x=fa[x]){
    		Splay(x),son[x][1]=y,Update(x);
    	}
    }
    inline void Makeroot(LL x){
    	Access(x),Splay(x),Pushr(x);
    }
    inline LL Findroot(LL x){
    	Access(x),Splay(x);
    	while(son[x][0]){
    		Pushdown(x),x=son[x][0];
    	}Splay(x);
    	return x;
    }
    
    inline void Split(LL x,LL y){
    	Makeroot(x),Access(y),Splay(y);
    }
    inline void Link(LL x,LL y){
    	Makeroot(x);
    	if(Findroot(y)!=x) fa[x]=y;
    }
    inline void Delet(LL x,LL y){
    	Split(x,y);
    	if(son[y][0]!=x||son[x][1]) return;
    	fa[x]=son[y][0]=0;
    	Update(y);
    }
    int main(){
    	n=Read(),m=Read();
    	for(LL i=1;i<=n;++i) val[i]=Read();
    	while(m--){
    		LL op(Read()),x(Read()),y(Read());
    		if(op==0){Split(x,y),printf("%d
    ",sum[y]);}
    		else if(op==1){Link(x,y);}
    		else if(op==2){Delet(x,y);}
    		else{Splay(x),val[x]=y;Update(x);}
    	}
    	return 0;
    }
    
  • 相关阅读:
    10.cocos2dx C++为Sprite添加触摸事件监听器
    9.多彩的幕布layer
    8.ZOrder
    7.cocos精灵创建和绘制
    6.cocos2d设置定时器
    5.cocos2d锚点
    4.cocos场景和层的调用
    文件导入导出
    两个整数相乘是否超限
    倒置字符串函数reverse
  • 原文地址:https://www.cnblogs.com/y2823774827y/p/10322083.html
Copyright © 2020-2023  润新知