题目链接:食物链
题目描述
动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。
现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是 1 X Y,表示 X 和 Y 是同类。
第二种说法是 2 X Y,表示 X 吃 Y 。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
当前的话与前面的某些真的话冲突,就是假话
当前的话中 X 或 Y 比 N 大,就是假话
当前的话表示 X 吃 X,就是假话
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入格式
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式
一行,一个整数,表示假话的总数。
输入输出样例
输入 #1
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输出 #1
3
说明/提示
1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5
题目分析
先判断是否是谎话
若是谎话
ans++;
continue;
若不是谎话
注释:
因为食物链包括 3 块,所以把并查集分为 3 块,把数组扩大为 3*n,每段并查集代表一个种类。
若是同类
merge(x, y);
merge(x+n, y+n);
merge(x+2n, y+2n);
注释:
同类时,使两个元素在 3 段并查集中都进行合并。
若不是同类
如果 x 吃 y:(大的吃小的)
merge(x+n, y);
merge(x+2n, y+n);
merge(x, y+2n);
注释:
不同类时,捕食者比捕食者的并查集大一段。
AC代码
#include <stdio.h>
const int maxn = 150100;
int f[maxn], h[maxn], n, k, d, x, y;
void Init() {
for(int i=1; i<=3*n; i++) { //注意 3 * n
f[i] = i;
h[i] = 0;
}
}
int Find(int i) {
return f[i]==i ? f[i] : f[i]=Find(f[i]);
}
void merge(int a, int b) {
int fa = Find(a);
int fb = Find(b);
if(fa != fb) {
if(h[fa] < h[fb]) {
f[fa] = fb;
} else {
f[fb] = fa;
if(h[fa]==h[fb]) h[fa]++;
}
}
}
int main(void) {
scanf("%d %d", &n, &k);
Init(); //初始化
int ans = 0; //记录谎话数量
while(k--) {
scanf("%d %d %d", &d, &x, &y);
if(x>n || y>n || (d==2 && x==y)) { //题干中第二和第三种情况
ans++;
continue;
}
//分情况讨论
if(d==1) { //同类时
if(Find(x)==Find(y+n) || Find(x+n)==Find(y)) {
//如果 X吃 Y,或 Y吃X
ans++;
continue;
}
merge(x, y);
merge(x+n, y+n);
merge(x+2*n, y+2*n);
} else if(d==2){ //不同类时
if(Find(x)==Find(y) || Find(x)==Find(y+n)) {
//如果 X和 Y同类,或 Y吃 X
ans++;
continue;
}
merge(x+n, y);
merge(x+2*n, y+n);
merge(x, y+2*n);
}
}
printf("%d\n", ans);
return 0;
}