POJ 1703 Find them, Catch them

这个题是并查集的扩展(也就是俗称的带权并查集

那么做这个题之前要先掌握并查集

并查集的内容其他网站已经有很多了,而且都讲的非常好,我就不重复造轮子了


我理解的带权并查集只是是在路径压缩的时候同时维护当前节点和根节点的一些关系

感觉好像就这么点东西Orz


先挂上一个题意好了,这个题是有两个帮派,还有一堆人,对于这一堆人中,每次有两个操作

D x y表示x和y是两个不同帮派的人

A x y是询问这两个人是不是一个帮派的


第一眼看到这题我的思路是用并查集,在同一个并查集中的人表示是同一个帮派的,理想很美好,现实很残酷(╯‵□′)╯︵┻━┻

如果这个题的D x y表示的是这两个人是同一个帮派的就好了


不过虽然第一个思路失败了,但是并查集还是要用的


但是该怎么用呢~?


你不禁开始回忆起并查集的作用,并查集是用来维护等价关系的

等价关系。。等价关系。。这个题有什么等价关系呢。。

题中给的是两个人是属于不同帮派的

属于不同帮派还能代表什么呢。。。

对了!

说明这两个人的关系是已知的(不准笑)

虽然我们弱化了这两个人的关系,但是如果从这个角度看的话,两个人的关系已知不就是我们要找的等价关系吗

(如果你不觉的这个很显然,请自行验证


既然有了一个等价关系,那么这个题就可以艹了


我们用两个点属于同一个集合表示他们的关系已知,这样的话,我们就可以用裸的并查集来判断是Not sure yet.还是其他的两个回答

但是要是已知了他们是同一个集合,也就是他们的关系已知之后,接下来该怎么做呢


裸的并查集还是不够的(笑


我们要在路径压缩的时候维护这个节点和他的父亲节点的关系(是属于同一个帮派还是分属于不同的帮派)


然后就可以干这个题了


---------------------我是代码的昏割线-------------------------------------

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
using namespace std;

const int maxn = 112345;

int arr[maxn];  // father node
int val[maxn];  //val == 1 ? same gang : different gang to arr[x];

int fnd(int x){
	if(arr[x]==x){
		return arr[x];
	}
	else{
		int root=fnd(arr[x]);
		if(val[arr[x]]==val[x])   //11 1|00 1|10 0|01 0
			val[x]=1;
		else 
			val[x]=0; 
		return arr[x]=root;
	}
} 

void join(int x,int y){
	int bx = x;
	int by = y;
	x = fnd(x);
	y = fnd(y);
	if(x==y)
		return;
	arr[x]=y;
	int to = 0;
	if(val[bx]==0) to ^=1; 
	if(val[by]==0) to ^=1;
	val[x]=to;
}

int que(int x,int y){
	int bx = x;
	int by = y;
	x = fnd(x);
	y = fnd(y);
	if(x!=y) return 0;
	if(val[bx]==val[by]) return 1;
	return -1;
}

void init(int n){
	for(int i=0;i<=n;i++)
		arr[i]=i,val[i]=1;
}

void out(int *s,int n){
	for(int i=1;i<=n;i++){
		printf(i<n?"%d ":"%d\n",s[i]);
	}
}

void out(int n){
	for(int i=1;i<=n;i++){
		printf(i<n?"%d ":"%d\n",i);
	}
}
int main(){
	int T;
	scanf("%d",&T);
	int n,m;
	char ord[3];
	while(T-- && ~scanf("%d %d",&n,&m)){
		init(n);
		while(m--){
			scanf("%s",ord);
			int x,y;
			scanf("%d %d",&x,&y);
			if(*ord=='A'){
				int req = que(x,y);
				if(req==0){
					puts("Not sure yet.");
				}
				else if(req==1){
					puts("In the same gang.");
				}
				else{
					puts("In different gangs.");
				}
			}
			else{
				join(x,y);
			}
//			out(n);
//			out(arr,n);
//			out(val,n);
//			puts("----");
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值