明辨是非 并查集题解
发布日期:2021-05-07 12:10:52 浏览次数:17 分类:原创文章

本文共 5294 字,大约阅读时间需要 17 分钟。

题目描述

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。

样例

输入

31 2 11 3 12 3 0

样例输出

YESYESNO

数据范围与提示

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;}
上一篇:[NOIP2018]普及组题解
下一篇:[JSOI2008]Blue Mary的战役地图 Hash题解

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月04日 01时05分04秒