题目描述
给 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 1≤n≤105, 1 ≤ x , y ≤ 1 0 8 1\leq x,y\leq10^8 1≤x,y≤108, p = 1 p=1 p=1 或 0 0 0。
分析
显然,这是一道并查集的题。因为
1
≤
x
,
y
≤
1
0
8
1\leq x,y\leq10^8
1≤x,y≤108,所以这道题需要离散化。
考虑
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;
}