题解: 很明显看到这种类别的合并和判断的题面一定就是并查集了, 但对于这题我们似乎不能够直接套模板来用, 因为无法直接确定种类, 而且要求得也只是假话的数量, 需要使用一点思维.
对于第i个动物a[i], 使用a[i], a[i+N], a[i+2*N], 分别表示i - A, i - B, i - C, 也就是当前动物和ABC三种动物的可能关系, 如果两数相同, 则可能同时发送
- 对于1操作, 我们合并(a[x], a[y]), (a[x+N], a[y+N]), (a[x+2N], a[y+2N])
- 对于2操作, 我们合并(a[x], a[y+N]), (a[x+N], a[y+2N]), (a[x+2N], a[y)
- 如果产生冲突无法合并则ans++
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const LL maxn = 5*1e4+10;
int N, K, opt, x, y;
int a[maxn*3], rk[maxn*3];
void init(int n){
for(int i = 1; i <= n; i++)
a[i] = i;
}
int findr(int n){
if(a[n] == n) return n;
else return a[n] = findr(a[n]);
}
bool isSame(int a, int b){ return findr(a)==findr(b);}
void unite(int x, int y){
x = findr(x), y = findr(y);
if(x == y) return;
if(rk[x] < rk[y])
a[x] = y;
else{
a[y] = x;
if(rk[x]==rk[y]) rk[x]++;
}
}
int main()
{
scanf("%d%d",&N,&K);
init(N*3); //表示 i-a, i-b, i-c的关系
int ans = 0;
while(K--){
scanf("%d%d%d",&opt,&x,&y);
if((opt!=1&&opt!=2) || x<1||x>N || y<1||y>N){
ans++;
continue;
}
if(opt == 1){
if(isSame(x, y+N) || isSame(x, y+2*N))
ans++; //冲突: x和y不是同一组
else
unite(x, y), unite(x+N, y+N), unite(x+2*N, y+2*N);
}else{
if(isSame(x, y)||isSame(x, y+2*N))
ans++; //冲突: x无法吃y
else
unite(x, y+N), unite(x+N, y+2*N), unite(x+2*N, y);
}
}
printf("%d\n",ans);
return 0;//
}