明辨是非 并查集题解

题目描述

n n n 组操作,每组操作形式为 x   y   p x\ y\ p x y p

p p p 1 1 1 时,如果第 x x x 个变量和第 y y y 个变量可以相等,则输出YES,并限制他们相等;否则输出 NO,并忽略此次操作。

p p p 0 0 0 时,如果第 x x x 个变量和第 y y y 个变量可以不相等,则输出 YES,并限制他们不相等;否则输出 NO,并忽略此次操作。

输入格式

第一行为一个正整数 n n n。 接下来 n n n 行每行三个整数: x x x y y y p p p

输出格式

对于 n n n 行操作,分别输出 n n n 行 YES 或者 NO。

样例

输入
3
1 2 1
1 3 1
2 3 0
样例输出
YES
YES
NO

数据范围与提示

1 ≤ n ≤ 1 0 5 1\leq n \leq 10^5 1n105 1 ≤ x , y ≤ 1 0 8 1\leq x,y\leq10^8 1xy108 p = 1 p=1 p=1 0 0 0

分析

显然,这是一道并查集的题。因为 1 ≤ x , y ≤ 1 0 8 1\leq x,y\leq10^8 1xy108,所以这道题需要离散化
考虑 p = 1 p=1 p=1 的情况,显然把这两个集合合并就行了。
难点在 p = 0 p=0 p=0的情况,因为 x ≠ y x\neq y x=y y ≠ z y \neq z y=z并不能推出 x = z x=z x=z,所以这个地方没有传递性。那怎么办呢?你可以用 v i s [ i ] [ j ] vis[i][j] vis[i][j]表示 i i i j j j不相等(只是数组不能开题目规定的这么大),还可以用 s e t set set,将与 i i i不相等的点都装进 s e t [ i ] set[i] set[i]中(为什么不用 v e c t o r vector vector呢?因为 s e t set set可以自动去重)。然而我们要考虑传递:
1.数组法(比较好写)。
p = 0 p=0 p=0,易知

 vis[i][j] = 1;//i != j
 vis[j][i] = 1;//同上

p = 1 p=1 p=1,易知

for(int k = 1; k <= num; k ++) {
     vis[i][k] |= vis[j][k];//若j != k则i != k
     vis[k][i] |= vis[k][j];//同上
     vis[j][k] = vis[i][k];//因为i = j,所以若i != k则j != k
     vis[k][j] = vis[k][i];//同上
}

2. s e t set set
p = 0 p=0 p=0

s[i].insert(j); //插入
s[j].insert(i); //同上

p = 1 p=1 p=1

if(rank[i] < rank[j]) {
	fa[i] = j;
	for(set <int>::iterator it = s[i].begin(); it != s[i].end(); it ++) {
		s[j].insert(*it);//因为i = j,所以若(*it) != i,则(*it) != j
	}
	for(set <int>::iterator it = s[j].begin(); it != s[j].end(); it ++) {
		s[*it].insert(j);//若(*it) != j,则将j加入到set[(*it)]中
	}
}
else {
	fa[j] = i;
	if(rank[i] == rank[j]) rank[i] ++;
	for(set <int>::iterator it = s[j].begin(); it != s[j].end(); it ++) {
		s[i].insert(*it);//因为i = j,所以若(*it) != j,则(*it) != i
	}
	for(set <int>::iterator it = s[i].begin(); it != s[i].end(); it ++) {
		s[*it].insert(i);//若(*it) != i,则将i加入到set[(*it)]中
	}
}

这里我用了启发式合并,因为 i = j i=j i=j,对于任意的 k k k,如果 i   ! = k i\ !=k i !=k(即 k k k s e t [ i ] set[i] set[i] 中),则 j ! = k j!=k j!=k(即 k k k s e t [ j ] set[j] set[j] 中)。同理, i i i j j j 都在 s e t [ k ] set[k] set[k] 中。

代码

法一

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
using namespace std;
const int MAXN = 2 * 1e5 + 5;
int n, fa[MAXN], rank[MAXN], lsh[MAXN], X[MAXN], Y[MAXN], Z[MAXN], cnt, num;
bool vis[25000][25000];
void Make_Set(int x) {
	for(int i = 1; i <= x; i ++) {//初始化
		fa[i] = i;
		rank[i] = 1;
	}
	return;
}
int Find_Set(int x) {//找父亲(路径压缩)
	if(x != fa[x]) {
		return fa[x] = Find_Set(fa[x]);
	}
	return fa[x];
}
bool Union_Set(int x, int y, int z) {//合并
	int i = Find_Set(x), j = Find_Set(y);
	if(z == 1) {
		if(i == j) return 1;
		if(vis[i][j]) return 0;
		if(rank[i] < rank[j]) fa[i] = j;
		else {
			fa[j] = i;
			if(rank[i] == rank[j]) rank[i] ++;
		}
		for(int k = 1; k <= num; k ++) {
			vis[i][k] |= vis[j][k];
			vis[k][i] |= vis[k][j];
			vis[j][k] = vis[i][k];
			vis[k][j] = vis[k][i];
		}
	}
	else {
		if(i == j) return 0;
		vis[i][j] = 1;
		vis[j][i] = 1;
	}
	return 1;
}
int main() {
	int x, y;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d%d%d", &X[i], &Y[i], &Z[i]);
		lsh[++ cnt] = X[i]; lsh[++ cnt] = Y[i]; 
	}
	sort(lsh + 1, lsh + 1 + cnt);//离散化
	num = unique(lsh + 1, lsh + 1 + cnt) - lsh - 1;
	Make_Set(num);
	for(int i = 1; i <= n; i ++) {
		x = lower_bound(lsh + 1, lsh + 1 + num, X[i]) - lsh;
		y = lower_bound(lsh + 1, lsh + 1 + num, Y[i]) - lsh;
		if(Union_Set(x, y, Z[i])) printf("YES\n");
		else {
			printf("NO\n");
		}
	}
	return 0;
}

法二

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#include <vector>
#include <set>
using namespace std;
const int MAXN = 2 * 1e5 + 5;
int n, fa[MAXN], rank[MAXN], lsh[MAXN], X[MAXN], Y[MAXN], Z[MAXN], cnt, num;
set <int> s[MAXN];
void Make_Set(int x) {//初始化
	for(int i = 1; i <= x; i ++) {
		fa[i] = i;
		rank[i] = 1;
	}
	return;
}
int Find_Set(int x) {//找父亲(路径压缩)
	if(x != fa[x]) {
		return fa[x] = Find_Set(fa[x]);
	}
	return fa[x];
}
bool Union_Set(int x, int y, int z) {//合并
	int i = Find_Set(x), j = Find_Set(y);
	if(z == 1) {
		if(i == j) return 1;
		set <int>::iterator it = s[i].find(j);
		set <int>::iterator it1 = s[j].find(i);
		if(it != s[i].end() || it1 != s[j].end()) return 0;
		if(rank[i] < rank[j]) {
			fa[i] = j;
			for(set <int>::iterator it = s[i].begin(); it != s[i].end(); it ++) {
				s[j].insert(*it);
			}
			for(set <int>::iterator it = s[j].begin(); it != s[j].end(); it ++) {
				s[*it].insert(j);
			}
		}
		else {
			fa[j] = i;
			if(rank[i] == rank[j]) rank[i] ++;
			for(set <int>::iterator it = s[j].begin(); it != s[j].end(); it ++) {
				s[i].insert(*it);
			}
			for(set <int>::iterator it = s[i].begin(); it != s[i].end(); it ++) {
				s[*it].insert(i);
			}
		}
	}
	else {
		if(i == j) return 0;
		s[i].insert(j); 
		s[j].insert(i);
	}
	return 1;
}
int main() {
	int x, y;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d%d%d", &X[i], &Y[i], &Z[i]);
		lsh[++ cnt] = X[i]; lsh[++ cnt] = Y[i]; 
	}
	sort(lsh + 1, lsh + 1 + cnt);//离散化
	num = unique(lsh + 1, lsh + 1 + cnt) - lsh - 1;
	Make_Set(num);
	for(int i = 1; i <= n; i ++) {
		x = lower_bound(lsh + 1, lsh + 1 + num, X[i]) - lsh;
		y = lower_bound(lsh + 1, lsh + 1 + num, Y[i]) - lsh;
		if(Union_Set(x, y, Z[i])) printf("YES\n");
		else {
			printf("NO\n");
		}
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值