zoukankan      html  css  js  c++  java
  • 浅谈LCT

    @

    动态树问题, ,近几年在OI中兴起的一种新型问题,是一类要求维护一个有根树森林,支持对树的分割,
    合并等操作的问题。由RobertE.Tarjan为首的科学家们提出解决算法Link-Cut Trees,简称lct。

    本段摘自百度
    要学lct,首先必要的基础必须有:

    有了这些基础,我们就可以开始学习lct。

    几个概念

    • Preferred Child:偏好儿子
    • Preferred Edge:偏好边
    • Preferred Path:偏好路径

    这几个概念跟树链剖分中的重儿子、重边和重链相似。唯一不同的地方就是树链剖分的重儿子、重边和重链是定的,而lct的是不定的,也就是说是没有限制的。

    Auxiliary Tree(辅助树)

    其实整棵lct就是又很多个散的splay tree由一些虚边连接而成的,而这些Splay tree就叫做辅助树,在程序实现的时候我们无需实现整棵lct,只需要维护所有Splay tree就好啦。
    在lct里的Splay是以深度为键值建的,就是说深度浅的一定在深度深的左边。

    维护什么?

    • son[0~1] 表示每个节点的两个儿子
    • pf表示每个节点的虚边连向的位置(当然不一定每个节点都有)
    • fath 表示每个节点在Splay tree 上的父亲

    其实大体上和Splay需要维护的东西不差多少,就多了一个虚边pf,这也是lct比较关键的部分。

    接下来我们来了解如何实现lct

    lct中的Splay

    朴素的Splay操作相信大家已经了解,那在lct中我们需要做什么额外的工作吗?

    void rotate(int x){
    	int y=t[x].fa,k=get(x);
    	if(t[y].fa) t[t[y].fa].son[get(y)]=x;
    	if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
    	t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
    	update(y),update(x),t[x].pf=t[y].pf;
    }
    

    rotate操作需要加上最后一句话,使得每次旋转的时候虚边都可以传递。

    核心操作:access(x)

    access(x)表示把从x这个点到根的路径上的点全部打成实边,并断掉下面所有的实边。
    这里写图片描述
    这个图应该比较形象,毕竟是杨哲集训队作业里的。
    实现比较简洁明了:

    Code

    void access(int x){
    	for (int y=0;x;update(x),y=x,x=t[x].pf){
    		splay(x,0);
    		t[t[x].son[1]].fa=0,t[t[x].son[1]].pf=x;
    		t[x].son[1]=y,t[y].fa=x,t[y].pf=0;
    	}
    }
    

    每次把右儿子断开,然后连上刚刚做过的那一个点。

    makeroot(x)操作

    这个操作的目的就是把x变成这个lct的根。
    这里用到Splay中的一个操作:翻转操作
    我们首先access(x),使得它到根的路径上变到同一棵Splay上面。
    此时x一定是这个Splay上最右边的点,然后就把它旋到根,做一个翻转,这样它就成为了最左边的点,也就是深度最小的点,也就是根。
    实现极其简单:

    Code:

    void make(int x){
    	swap(t[x].son[0],t[x].son[1]);
    	t[x].tag^=1;
    }
    void makeroot(int x){
    	access(x),splay(x,0);
    	make(x);
    }
    

    findroot(x)操作

    这是用来判断两点是否在同一个连通块上的利器,就像并查集的getfather操作一样,这个实现更简单。首先我们access(x),这样就可以使得他和根在同一Splay上,然后直接找到Splay最左边的那个点就是了。
    实现同样及其简短:

    Code

    int findroot(int x)
    {
    	access(x),splay(x,0);
    	for (;t[x].son[0];x=t[x].son[0]);
    	return x;
    }
    

    Link(x,y)操作

    使得x,y在lct上连接起来。
    首先让x变成根,这样它就没有虚边指向的地方了。
    直接上代码,太简短:

    Code:

    void link(int x,int y){
    	makeroot(x);
    	if(findroot(y)!=x) t[x].pf=y;
    }
    

    Cut(x,y)操作

    切断x,y之间的连边。
    一样是首先让x变成根,然后access(y),Splay(y,0)如果他们之间有连边那么x一定是y的儿子,直接断开。

    Code:

    void cut(int x,int y){
    	makeroot(x),access(y),splay(y,0);
    	if(t[x].fa==y) t[y].son[get(x)]=t[x].fa=t[x].pf=0;
    }
    

    应用:

    待博主更新...

    Exanple

    P3690 【模板】Link Cut Tree (动态树)

    #include<cstdio>
    #include<iostream>
    #define maxn 300010
    using namespace std;
    struct Moon{
    	int pf,fa,son[2],key,sum,tag;
    }t[maxn]; 
    int n,m,kind,d[maxn];
    void make(int x){
    	swap(t[x].son[0],t[x].son[1]);
    	t[x].tag^=1;
    }
    void update(int x){
    	t[x].sum=t[t[x].son[0]].sum^t[t[x].son[1]].sum^t[x].key;
    }
    void clear(int x){
    	if(t[x].tag){
    		make(t[x].son[0]),make(t[x].son[1]);
    		t[x].tag=0;
    	}
    }
    void remove(int x,int y){
    	for (d[0]=0;x!=y;x=t[x].fa) d[++d[0]]=x;
    	for (int i=d[0];i>=1;--i) clear(d[i]);
    }
    int get(int x){
    	return t[t[x].fa].son[1]==x;
    }
    void rotate(int x){
    	int y=t[x].fa,k=get(x);
    	if(t[y].fa) t[t[y].fa].son[get(y)]=x;
    	if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
    	t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
    	update(y),update(x),t[x].pf=t[y].pf;
    }
    void splay(int x,int y){
    	remove(x,y);
    	while(t[x].fa!=y){
    		if(t[t[x].fa].fa!=y)
    			if(get(x)==get(t[x].fa)) rotate(t[x].fa);
    			else rotate(x);
    		rotate(x);
    	}
    }
    void access(int x){
    	for (int y=0;x;update(x),y=x,x=t[x].pf){
    		splay(x,0);
    		t[t[x].son[1]].fa=0,t[t[x].son[1]].pf=x;
    		t[x].son[1]=y,t[y].fa=x,t[y].pf=0;
    	}
    }
    int findroot(int x)
    {
    	access(x),splay(x,0);
    	for (;t[x].son[0];x=t[x].son[0]);
    	return x;
    }
    void makeroot(int x){
    	access(x),splay(x,0);
    	make(x);
    }
    int query(int x,int y){
    	makeroot(x),access(y);
    	splay(y,0);return t[y].sum;
    }
    void link(int x,int y){
    	makeroot(x);
    	if(findroot(y)!=x) t[x].pf=y;
    }
    void cut(int x,int y){
    	makeroot(x),access(y),splay(y,0);
    	if(t[x].fa==y) t[y].son[get(x)]=t[x].fa=t[x].pf=0;
    }
    void change(int x,int y){
    	splay(x,0);
    	t[x].key=y,update(x);
    }
    int main(){
    	int i,x,y;
    	scanf("%d%d",&n,&m);
    	for (i=1;i<=n;++i) scanf("%d",&t[i].key);
    	while(m--){
    		scanf("%d%d%d",&kind,&x,&y);
    		if(kind==0)printf("%d\n",query(x,y));
    		if(kind==1)link(x,y);
    		if(kind==2)cut(x,y);
    		if(kind==3)change(x,y);
    	}
    } 
    

    P3203 [HNOI2010]弹飞绵羊

    #include<cstdio>
    #include<iostream>
    #define maxn 200010
    #define min(x,y) (((x)<(y))?(x):(y))
    using namespace std;
    struct Moon{
    	int pf,fa,son[2],key,size,tag;
    }t[maxn]; 
    int n,m,kind,d[maxn];
    int get(int x){
    	return t[t[x].fa].son[1]==x;
    }
    void update(int x){
    	t[0].size=0;
    	t[x].size=t[t[x].son[0]].size+t[t[x].son[1]].size+1;
    }
    void rotate(int x){
    	int y=t[x].fa,k=get(x);
    	if(t[y].fa) t[t[y].fa].son[get(y)]=x;
    	if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
    	t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
    	update(y),update(x),t[x].pf=t[y].pf;
    }
    void change(int x){
    	swap(t[x].son[0],t[x].son[1]);
    	t[x].tag^=1;
    }
    void clear(int x){
    	if(t[x].tag){
    		change(t[x].son[0]),change(t[x].son[1]);
    		t[x].tag=0;
    	}
    }
    void remove(int x,int y){
    	for (d[0]=0;x!=y;x=t[x].fa) d[++d[0]]=x;
    	for (int i=d[0];i>=1;--i) clear(d[i]);
    }
    void splay(int x,int y){
    	remove(x,y);
    	while(t[x].fa!=y){
    		if(t[t[x].fa].fa!=y)
    			if(get(x)==get(t[x].fa)) rotate(t[x].fa);
    			else rotate(x);
    		rotate(x);
    	}
    }
    void access(int x){
    	for (int y=0;x;update(x),y=x,x=t[x].pf){
    		splay(x,0);
    		t[t[x].son[1]].fa=0,t[t[x].son[1]].pf=x;
    		t[x].son[1]=y,t[y].fa=x,t[y].pf=0;
    	}
    }
    void makeroot(int x){
    	access(x),splay(x,0);
    	change(x);
    }
    int query(int x){
    	makeroot(n+1),access(x);
    	splay(x,0);
    	return t[x].size-1;
    }
    void cut(int x,int y){
    	makeroot(x);
    	access(y);
    	splay(y,0);
    	t[y].son[get(x)]=t[x].fa=t[x].pf=0;
    }
    void link(int x,int y){
    	makeroot(x),t[x].pf=y;
    }
    int main(){
    	scanf("%d",&n);
    	int i,j,x,y;
    	for (i=1;i<=n;++i){
    		scanf("%d",&t[i].key);
    		link(i,min(n+1,i+t[i].key));
    	}
    	scanf("%d",&m);
    	while(m--){
    		scanf("%d",&kind);
    		if(kind==1){
    			scanf("%d",&x);++x;
    			printf("%d\n",query(x));
    		}
    		if(kind==2){
    			scanf("%d%d",&x,&y);x++;
    			cut(x,min(n+1,x+t[x].key));
    			t[x].key=y;
    			link(x,min(n+1,x+t[x].key));
    		}
    	}
    }
    

    【NOIP2018模拟A组9.25】到不了

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define maxn 100010
    #define maxm 100010
    #define maxq 100010
    using namespace std;
    int n,m,q,tot;
    int root[maxm],d[maxn];
    struct Moon{
    	int pf,fa,son[2],tag;
    }t[maxn];
    int fa[maxn];
    void make(int x){
    	swap(t[x].son[0],t[x].son[1]);
    	t[x].tag^=1;
    }
    void clear(int x){
    	if(t[x].tag){
    		make(t[x].son[0]),make(t[x].son[1]);
    		t[x].tag=0;
    	}
    }
    void remove(int x,int y){
    	for (d[0]=0;x!=y;x=t[x].fa) d[++d[0]]=x;
    	for (int i=d[0];i>=1;--i) clear(d[i]);
    }
    int get(int x){
    	return t[t[x].fa].son[1]==x;
    }
    void rotate(int x){
    	int y=t[x].fa,k=get(x);
    	if(t[y].fa) t[t[y].fa].son[get(y)]=x;
    	if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
    	t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
    	t[x].pf=t[y].pf;
    }
    void splay(int x,int y){
    	remove(x,y);
    	while(t[x].fa!=y){
    		if(t[t[x].fa].fa!=y)
    			if(get(x)==get(t[x].fa)) rotate(t[x].fa);
    			else rotate(x);
    		rotate(x);
    	}
    }
    int access(int x){
    	int z;
    	for (int y=0;x;y=x,x=t[x].pf){
    		splay(x,0);
    		t[t[x].son[1]].fa=0,t[t[x].son[1]].pf=x;
    		t[x].son[1]=y,t[y].fa=x,t[y].pf=0,z=x;
    	}
    	return z;
    }
    int Get(int x){
    	if(fa[x]==x) return x;
    	return fa[x]=Get(fa[x]);
    }
    void makeroot(int x){
    	access(x);
    	splay(x,0);
    	make(x);
    }
    void link(int x,int y){
    	makeroot(y);
    	int xx=Get(x),yy=Get(y);
    	if(xx!=yy) fa[xx]=yy,t[y].pf=x;
    }
    int findans(int x,int y){
    	if(Get(x)!=Get(y)) return -1;
     	int q=access(y);
    	int p=access(x);
    	return p;
    }
    int main(){
    	freopen("arrival.in","r",stdin);
    	freopen("arrival.out","w",stdout);
    	scanf("%d %d",&n,&m);
    	int i,j,x,y,kind,ans;
    	for (i=1;i<=m;++i) scanf("%d",&root[i]);
    	for (i=1;i<=n;++i) fa[i]=i;
    	for (i=1;i<=n-m;++i){
    		scanf("%d %d",&x,&y);
    		link(x,y);
     	}
     	for (i=1;i<=m;++i) makeroot(root[i]);
    	scanf("%d",&q);
    	while(q--){
    		scanf("%d%d%d",&kind,&x,&y);
    		if(kind==1){
    			if(Get(x)!=Get(y))link(x,y);
    		}
    		else{
    			ans=findans(x,y);
    			if(ans==-1) printf("orzorz\n");
    			else printf("%d\n",ans);
    		}
    	}
    }
    
  • 相关阅读:
    CF 234 C Weather(粗暴方法)
    给工作赋予的新意义——Leo鉴书78
    获取集合的方法
    VS 统计代码行数
    , ,
    指针的删除动作
    C++ 名称空间
    boost::token_compress_on
    指针与引用
    容器的end()方法
  • 原文地址:https://www.cnblogs.com/Chandery/p/11332787.html
Copyright © 2011-2022 走看看