「NOI2015」程序自动分析 并查集题解
发布日期:2021-05-07 12:10:50 浏览次数:6 分类:原创文章

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

题目描述

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。

考虑一个约束满足问题的简化版本:假设 x 1 x_1 x1, x 2 x_2 x2, x 3 x_3 x3,…代表程序中出现的变量,给定 n n n个形如 x i = x j x_i=x_j xi=xj x i ≠ x j x_i≠x_j xi=xj的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为: x 1 = x 2 x_1=x_2 x1=x2 x 2 = x 3 x_2=x_3 x2=x3 x 3 = x 4 x_3=x_4 x3=x4 x 1 ≠ x 4 x_1≠x_4 x1=x4,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
现在给出一些约束满足问题,请分别对它们进行判定。

输入格式

输入文件的第 1 1 1行包含 1 1 1个正整数 t t t,表示需要判定的问题个数。注意这些问题之间是相互独立的。

对于每个问题,包含若干行:
1 1 1行包含 1 1 1个正整数 n n n,表示该问题中需要被满足的约束条件个数。
接下来 n n n行,每行包括 3 3 3个整数 i i i, j j j, e e e,描述 1 1 1个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 e = 1 e=1 e=1,则该约束条件为 x i = x j x_i=x_j xi=xj;若 e = 0 e=0 e=0,则该约束条件为 x i ≠ x j x_i≠x_j xi=xj

输出格式

输出文件包括 t t t行。 输出文件的第 k k k行输出一个字符串“YES”或者“NO”(不包含引号,字母全部大写),“YES”表示输入中的第 k k k个问题判定为可以被满足,“NO”表示不可被满足。

样例

输入

221 2 11 2 021 2 12 1 1

输出

NOYES

数据范围与提示

对于所有的数据, 1 ≤ t ≤ 10 1 \leq t \leq 10 1t10 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106 1 ≤ i , j ≤ 1 0 9 1 \leq i,j \leq 10^9 1i,j109

分析

显然,这是一道并查集的题。
因为 i , j i,j i,j两数有可能很大, f a fa fa数组很可能装不下,而 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106 f a fa fa数组是可以装下的,所以考虑离散化
做法:循环一遍,将 i = j i=j i=j的情况存在 f a fa fa数组中。再循环一遍,考虑 i ≠ j i \neq j i=j的情况,若 f a fa fa数组中 i i i j j j的父亲相等了,就说明矛盾了。
时间复杂度: O ( T n l o g 2 ( n ) ) O(Tnlog_2(n)) O(Tnlog2(n))

代码

#include <cstdio>#include <algorithm>#include <cmath>#include <climits>#include <cstring>using namespace std;const int MAXN = 1e6 + 5;int fa[MAXN], Rank[MAXN], cnt, X[MAXN], Y[MAXN], Z[MAXN], lsh[MAXN << 1], Size, num[MAXN][2];bool flag;void read(int &x) {   //读优    int f = 1;    x = 0;    char c = getchar();    while(c < '0' || c > '9') {           if(c == '-') f = -1;        c = getchar();    }    while(c >= '0' && c <= '9') {           x = x * 10 + (c - '0');        c = getchar();    }    x *= f;}void Make_Set(int n) {   //初始化	for(int i = 1; i <= n; i ++) {   		fa[i] = i;		Rank[i] = 0;	}	return;}int Find_Set(int x) {   //找父亲(路径压缩)	if(fa[x] != x) {   		return fa[x] = Find_Set(fa[x]); 	}	return fa[x];}void Union_Set(int x, int y) {   //按秩合并	int i = Find_Set(x), j = Find_Set(y);	if(i == j) return;	if(Rank[i] < Rank[j]) fa[i] = j;	else {   		fa[j] = i;		if(Rank[i] == Rank[j]) Rank[i] ++;	}	return;}bool Union_Sets(int x, int y) {   //判断是否矛盾	int i = Find_Set(x), j = Find_Set(y);	if(i == j) return 1;	return 0;}int main() {   	int T, n; 	read(T);	while(T --) {   		memset(lsh, 0, sizeof(lsh));		cnt = 0; flag = 1;		read(n);		for(int i = 1; i <= n; i ++) {   			read(X[i]); read(Y[i]); read(Z[i]);			lsh[++ cnt] = X[i];			lsh[++ cnt] = Y[i];		}		sort(lsh + 1, lsh + 1 + cnt);		Size = unique(lsh + 1, lsh + 1 + cnt) - lsh - 1;		Make_Set(Size);		for(int i = 1; i <= n; i ++) {   			num[i][0] = lower_bound(lsh + 1, lsh + 1 + Size, X[i]) - lsh;//离散化			num[i][1] = lower_bound(lsh + 1, lsh + 1 + Size, Y[i]) - lsh;		}		for(int i = 1; i <= n; i ++) {   			if(Z[i] == 1) Union_Set(num[i][0], num[i][1]);		}		for(int i = 1; i <= n; i ++) {   			if(Z[i] == 0 && Union_Sets(num[i][0], num[i][1])) {   				flag = 0;				printf("NO\n");				break;			}		}		if(flag) {   			printf("YES\n");		}	}	return 0;}
上一篇:[JSOI2008]Blue Mary的战役地图 Hash题解
下一篇:TSP问题(旅行商问题) 状压DP求解

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年04月05日 13时38分56秒