2018.10.07【SDOI2008】【BZOJ2049】【洛谷P2147】Cave洞穴勘测(LCT)

BZOJ传送门

洛谷传送门


解析:

这是一道LCTLCTLCT的裸题,却不够板。

思路:

这是LCTLCTLCT的一个经典应用,维护动态树上节点的联通性。
对于基本操作我不再赘述,详见我的LCTLCTLCT模板(暂未更新)。

这道题就讲一讲怎么维护联通性。

由于同一联通块中的节点是由几条被SplaySplaySplay维护的重链所覆盖的,我们判断连通性的时候只需要将一个节点变为该联通子树的根,然后判断另一个节点的SplaySplaySplay根是否是它就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline
char getalpha(){
	re char c;
	while(!isalpha(c=gc()));
	return c;
}

typedef struct splay_node *point;
struct splay_node{
	point son[2],fa;
	bool tag;
	void init(){son[0]=son[1]=fa=NULL,tag=0;}
	void pushdown(){
		if(tag){
			if(son[0])son[0]->tag^=1;
			if(son[1])son[1]->tag^=1;
			swap(son[0],son[1]);
			tag=0;
		}
	}
	point &lc(){return son[0];}
	point &rc(){return son[1];}
	bool isroot(){return !fa||(fa->lc()!=this&&fa->rc()!=this);}
	bool which(){return this==fa->son[1];}
};

cs int N=10004;
struct Link_Cut_Tree{
	point tr[N];
	void init(int n){
		for(int re i=0;i<=n;++i)
		tr[i]=(point)malloc(sizeof(splay_node)),
		tr[i]->init();
	}
	
	void Rotate(point now){
		point Fa=now->fa,FA=Fa->fa;
		if(FA&&!Fa->isroot())FA->son[Fa->which()]=now;
		Fa->fa=now;
		now->fa=FA;
		bool pos=now==Fa->lc();
		point tmp=now->son[pos];
		if(tmp)tmp->fa=Fa;
		Fa->son[!pos]=tmp;
		now->son[pos]=Fa;
	}
		
	void Splay(point now){
		static point q[N];
		static int qn;
		q[qn=1]=now;
		for(point Fa=now;!Fa->isroot();Fa=Fa->fa)q[++qn]=Fa->fa;
		for(int re i=qn;i;--i)q[i]->pushdown();
		for(point Fa=now->fa;!now->isroot();Rotate(now),Fa=now->fa)
		if(!Fa->isroot())Rotate(now->which()==Fa->which()?Fa:now);
	}
	
	void access(point now){
		for(point son=NULL;now;son=now,now=now->fa){
			Splay(now);now->rc()=son;
			if(son)son->fa=now;
		}
	}
	
	void makeroot(point now){
		access(now);
		Splay(now);
		now->tag^=1;
	}
	
	point findroot(point now){
		access(now);
		Splay(now);
		while(now->lc())now=now->lc();
		return now;
	}
	
	void link(point u,point v){makeroot(u);u->fa=v;}
	void Link(int u,int v){link(tr[u],tr[v]);}
	
	void cut(point u,point v){
		makeroot(u),access(v);
		Splay(v);
		u->fa=NULL;
		v->lc()=NULL;
	}
	void Cut(int u,int v){cut(tr[u],tr[v]);}
	
	bool connect(point u,point v){
		makeroot(u);
		point tmp=findroot(v);
		return tmp==u;
	}
	bool Connect(int u,int v){return connect(tr[u],tr[v]);}
	
}LCT;
int n,m;
signed main(){
	n=getint(),m=getint();
	LCT.init(n);
	while(m--){
		char op=getalpha();
		int u=getint(),v=getint();
		switch(op){
			case 'C':{
				LCT.Link(u,v);
				break;
			}
			case 'D':{
				LCT.Cut(u,v);
				break;
			}
			case 'Q':{
				puts(LCT.Connect(u,v)?"Yes":"No");
				break;
			}
		}
	}
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047253.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值