题意:
有三种动物,A吃B,B吃C,C吃A。
现有K组语句,n个动物,1 x y:x和y是同类,2 x y:x吃y。
符合下列三个条件之一的即为错误语句,输出错误语句个数。
1.自己吃自己
2.x > n || y > n
3.当前语句和之前的正确语句发生了冲突
思路:
本题是拓展域并查集的典型题目,利用拆点,将每一个点拆成3个点,分别表示本身,该点的天敌,该点的猎物,然后对于K组语句再一一进行合并和判断,问题就可以迎刃而解。
总结:
此类真假问题,或者一个点有多个状态的问题,需要优先考虑是否可以通过并查集得出结果,将一个点进行拓展,巧妙地解决问题。
但是当情况更加复杂的时候,拓展域并查集就不能再这么好的解决问题,到那个时候我们就需要引出图论中的 2-SAT 解法。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 15*1e4+100;
int fa[N],n,k;
int get(int x)
{
if(x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
int main()
{
scanf("%d%d",&n,&k);
rep(i,1,3*n) fa[i] = i;
int cnt = 0;
rep(i,1,k)
{
int x,y,z;
scanf("%d%d%d",&z,&x,&y);
int fx1 = get(x), fx2 = get(x+n), fx3 = get(x+2*n);
int fy1 = get(y), fy2 = get(y+n), fy3 = get(y+2*n);
if(y > n || x > n){cnt++; continue;}
if(z == 1)
{
if(fx1 == fy2 || fx1 == fy3 || fx2 == fy1 || fx2 == fy3 || fx3 == fy1 || fx3 == fy2)
{
cnt++;
continue;
}
fa[fx1] = fy1, fa[fx2] = fy2, fa[fx3] = fy3;
}
else{
if(y == x || fx1 == fy1 || fx1 == fy3)
{
cnt++;
continue;
}
fa[fx1] = fy2, fa[fx2] = fy3, fa[fx3] = fy1;
}
}
printf("%d\n",cnt);
return 0;
}