题目链接: https://www.luogu.org/problemnew/show/P2024
题意:有三种生物A,B,C,它们的关系是A吃B,B吃C,C吃A。现在给出一些语句,判断是否是假话,输出假话数量。
输入类似 a x y,如果a等于1,说明x,y是同类,a等于2说明x是y的天敌。
思路:经典的并查集题目,用x表示生物本身,x+n表示它的猎物,x+2*n表示它的敌人,具
体请看代码注释。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
const int maxn = 5e4+2;//生物数量,分为三类:A,B,C
int N, K, ans = 0;;//K是语句数量,ans是假话数量
int fa[3 * maxn];
int read(){//快读
int x=0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x;
}
int find(int x){//并查集查询操作
if(x == fa[x]) return fa[x];
return fa[x] = find(fa[x]);
}
int unity(int x, int y){//合并操作
int a = find(fa[x]),b = find(fa[y]);
fa[a] = b;
}
int main(){
N = read(); K = read();
for(int i = 1; i <= 3 * N; i++)//这里是3*N
fa[i] = i;//初始化
for(int i = 1; i <= K; i++){
int a, x, y;
a = read(); x = read(); y = read();
if(x > N || y > N){//不在范围类,当然是假话
ans++;
continue;
}
if(a == 1){//如果x,y是同类
if(find(x + N) == find(y) || find(x + 2*N) == find(y)){
ans++;//如果x的猎物是y或者x的天敌是y,肯定不行
continue;
}
unity(x, y);//二者是同类
unity(x + N, y + N);//猎物相同
unity(x + 2*N, y + 2*N);//天敌相同
}
else if(a == 2){//如果x的猎物是y
if(x == y){//不能相等,可有可无吧
ans++;
continue;
}
if(find(x + 2*N) == find(y) || find(y) == find(x)){
ans++;//y不能是x的天敌和同类
continue;
}
unity(x, y + 2*N);//x是y的天敌
unity(x + N, y);//x的猎物是y
unity(x + 2*N, y + N);//根据A吃B,B吃C,C吃A,x的天敌是y的猎物
}
}
printf("%d\n",ans);//输出
return 0;
}
总结:并查集用于处理集合的合并问题真是太强大了。