题目描述
在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设
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”表示不可被满足。
样例
输入
2
2
1 2 1
1 2 0
2
1 2 1
2 1 1
输出
NO
YES
数据范围与提示
对于所有的数据, 1 ≤ t ≤ 10 1 \leq t \leq 10 1≤t≤10, 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106, 1 ≤ i , j ≤ 1 0 9 1 \leq i,j \leq 10^9 1≤i,j≤109。
分析
显然,这是一道并查集的题。
因为
i
,
j
i,j
i,j两数有可能很大,
f
a
fa
fa数组很可能装不下,而
1
≤
n
≤
1
0
6
1 \leq n \leq 10^6
1≤n≤106,
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;
}