这题也可以用边带权并查集,但是我觉得扩展域并查集更好理解。
对于每个点x 有三个域 分别是 同类域,捕食域和天敌域
当x和y是同类时,显然x和y的三个域对应相同
当x吃y时,x的同类域和y的天敌域并起来,x的捕食域和y的同类域并起来,由题目知道x->y->z 之间成环 所以z是吃x的 所以x的天敌域要和y的捕食域并起来
在并之前判断是否为真话
如果x和y是同类 有两个冲突:
1.x的捕食域和y的同类域是一个集合(x吃y)
2.x的同类域和y的捕食域是一个集合(y吃x)
如果x吃y 也有两个冲突
1.x的同类域和y的同类域是一个集合(x和y是同类)
2.y的捕食域和x的同类域是一个集合(y吃x)
我们用 x,x+n,x+2*n分别代表x的同类域,捕食域和天敌域 具体实现看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200100;
int fa[N],n,k;
void init(){
for(int i = 1; i <= 3*n; i++) fa[i]=i;
}
int get(int x){
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
int main(){
scanf("%d%d",&n,&k);
int ans = 0;
init();
for(int i = 1; i <= k; i++){
int x,y,d;
scanf("%d%d%d",&d,&x,&y);
if((d==2&&x==y)||(x>n)||(y>n)){
ans++;
continue;
}else{
if(d==1){
if(get(x)==get(y+n)||get(x+n)==get(y)){
ans++;
continue;
}
fa[get(x)]=get(y),fa[get(x+n)]=get(y+n),fa[get(x+2*n)]=get(y+2*n);
}else{
if(get(x)==get(y)||get(y+n)==get(x)){
ans++;
continue;
}
fa[get(x+n)]=get(y),fa[get(x)]=get(y+2*n),fa[get(x+2*n)]=get(y+n);
}
}
//printf("i=%d ans=%d\n",i,ans);
}
printf("%d\n",ans);
return 0;
}