题目是中文,不再写题目描述。
这个题主要说一个思路
自上而下进行操作的时候,如果遇到操作1,表示将后续的两个生物列为同一类,矛盾的条件是后面的两个生物 a , b 存在捕食关系,也就是说如果 a 捕食 b 或者 b 捕食 a 则是假的。
操作2而言,表示后续两个生物 a 和 b 中, a 捕食 b ,矛盾条件是 a 和 b 已知是同类生物或者已知 b 捕食 a ,以为题目说明了只有三类生物A,B,C且捕食关系成环形,因此可以列出捕食域,同类域,天敌域
操作1判断条件:
- a 捕食 b ,即 a 的捕食域和 b 的同类域在同一集合
- b 捕食 a ,即 b 的捕食域和 a 的同类域在同一集合
操作2判断条件:
- a 和 b 是同类,即 a 的同类域和 b 的同类域在同一集合
- b 捕食 a,即 b 的捕食域和 a 的同类域在同一集合或者 a 的天敌域和 b 的同类域在同一集合
合并的时候,对于操作1,将 a 和 b 的同类域、捕食域、天敌域对应的合并即可,对于操作2,需要将 a 的捕食域和 b 的同类域合并,将 a 的同类域和 b 的天敌域合并,此外,根据题目描述的环形捕食关系可以看出还需要将 a 的天敌域和 b 的捕食域合并
对每一个区域的定义,是将 root 数组开大,对于题目输入的 n ,a 的同类域是 root[ n ],捕食域是 root[a + n],天敌域是root[a + n + n],这样可以错开,一开始我写的时候写出了 a,a + a,a + a + a,这样的话如果 n = 10,a = 1的时候天敌域和捕食域与其他的生物的同类域等会重复,用 n 可以跳过他们的对应区域,不会重复
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, k;
int root[maxn * 5];
int find(int a){
return root[a] == a ? a : root[a] = find(root[a]);
}
void mer(int a, int b){
int fa = find(a);
int fb = find(b);
if(fa != fb)root[fa] = fb;
}
int main()
{
scanf("%d %d", &n, &k);
for (int i = 0; i < maxn * 5; i++)root[i] = i;
int res = 0;
while (k--){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
if(b > n || c > n)++res;
else if(a == 1){
if(find(b) == find(c + n) || find(b + n) == find(c))++res;
else{
mer(b, c);
mer(b + n, c + n);
mer(b + n + n, c + n + n);
}
}
else if(a == 2){
if(b == c || find(b) == find(c) || find(c + n) == find(b)){
++res;
}
else{
mer(b + n, c);
mer(b, c + n + n);
mer(b + n + n, c + n);
}
}
}
printf("%d\n", res);
return 0;
}
另:该题还有另外的解法,不是用拓展域,而是用边带权的并查集,日后补上