1007 - 动态树(LCT)- 洞穴勘探(BZOJ 2049)

传送门

 

题意

一颗树支持:

  1. 连接两个点
  2. 删除两个点之间的边
  3. 询问两个点是否连通

 

分析

第一次做LCT的题,有点小激动,虽然这只是一道模板题而已。

由题意可知我们需要进行三个操作:link,cut,query

对于连接(x,y)这条边,我们先 makeroot(x) ,再将 x 的 fa 置为 y 

删除的话,先makeroot(x),再access(y)【取出 y 到 x 的这条路径,且当前状态下这条路径上只有 x 和 y 】,splay(y)

然后将 lc(y) = 0; fa(x) = 0;

询问的话就更简单了,直接比较 x 和 y 所在树的根节点是否相同

 

小小牢骚 

一下午的调试很崩溃啊

  1. 用宏命令,小括号太多,手贱,打错了一个
  2. 在splay的时候忘记更新fa的值了
  3. rotate的时候顺序有bug

(这是拿命在调代码啊!!!,感谢zxy大佬Orz,感谢dzy大佬Orz)

如果要别人帮忙调代码的话,真的很麻烦人家,希望自己可以自己多想想,别老是麻烦人家

调试心得:

最好不要用宏命令,特别是带括号的那一种,不然完全分不清是函数还是什么鬼啊,而且一不小心手滑,括号就出锅了,而且根本检查不出来啊

 

 

代码

//虽然我很菜,但我也会很努力! 
#include<bits/stdc++.h>
#define in read()
#define N 10009
#define M 400009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m,u,v,que[N],qr=0;
char st[10];
struct node{
	int lc,rc,fa,lzy;
	#define fa(x) p[x].fa
	#define lc(x) p[x].lc
	#define rc(x) p[x].rc
	#define lzy(x) p[x].lzy
}p[N];
void pushdown(const int &k){
	if(lzy(k)){
		swap(lc(k),rc(k));
		lzy(lc(k))^=1;
		lzy(rc(k))^=1;
		lzy(k)=0;
	}
	return ;
}
bool isroot(const int &x){
	if(!fa(x)) return 1;
	if((lc(fa(x))!=x)&&(rc(fa(x))!=x)) return 1;//括号出锅 
	return 0;
}
void rotate(const int &x){
	int y=fa(x),z=fa(y),a=(lc(y)==x)?rc(x):lc(x);
	if(z&&!isroot(y)) ((lc(z)==y)?lc(z):rc(z))=x;
	fa(x)=z;fa(y)=x;
	if(a)  fa(a)=y;
	if((lc(y)==x)) rc(x)=y,lc(y)=a;
	else lc(x)=y,rc(y)=a;
	return ;
}
void splay(const int &x){// 由于打了标记,因此根到当前需要Splay 的节点的路径上的标记需要全部下放
	que[qr=0]=x;
	for(int y=x;!isroot(y);y=fa(y)) que[++qr]=fa(y);
	for(int i=qr;i>=0;--i) pushdown(que[i]);//pushdown!!! 
	int y=fa(x),z=fa(y);
	while(!isroot(x)){
		y=fa(x),z=fa(y);//更新 
		if(!isroot(y)){
			if((x==lc(y))==(y==lc(z))) rotate(y);
			else rotate(x);
		}
		rotate(x);//
	}
	return ;
}
void access(int x){
	for(int y=0;x;y=x,x=fa(x)){
		splay(x);rc(x)=y;
	}
	return ;
}
void makeroot(const int &x){
	access(x);splay(x);
	lzy(x)^=1;return;
}
void link(const int &x,const int &y){
	makeroot(x);fa(x)=y;
	return;
}
void cut(const int &x,const int &y){
	makeroot(x);access(y);splay(y);
	lc(y)=0;fa(x)=0;
	return;
}
int findroot(int x){
	access(x);splay(x);//after every operation u have to splay
	while(pushdown(x),lc(x)) x=lc(x);
	return x;
}
int main(){
	n=in;m=in;
	while(m--){
		scanf("%s",st);
		u=in;v=in;
		if(st[0]=='C')	link(u,v);
		else{ if(st[0]=='D') cut(u,v);
			 else {
				if(findroot(u)==findroot(v)) printf("Yes\n");
				else printf("No\n");
			}
  		}
	}
	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值