题目连接: 食物链
大致题意:
有三种动物, 共有N个, 组成A->B->C->A的食物链关系, 有k个说法, 让你判断其中的假话.
假话: ①前面的真话冲突 ②动物编号超过N ③同类吃同类
解题思路:
并查集. 维护集合中节点到根节点的距离(%3计算).
假设某种动物是0(根节点), 则吃他的生物为1, 被他吃的生物为2, 同类则也为0. (距离%3后的结果)
我们在进行判断的时候, 一定是判断当前这个生物与他所在集合中根节点的情况, 所以找查函数中, 我们必须要对每个节点进行路径压缩, 并且将非根父节点的距离增加到当前节点上.
我们可以得到结果, 如果x与y在一个集合内, 且到根节点距离相同 ==> x与y同种生物. 如果到根节点距离差1 ==> y吃x, 如果差2 ==> x吃y
AC代码:
#include <cstdio>
typedef long long ll;
using namespace std;
const int N = 5E4 + 10;
int p[N], d[N];
int find(int x) {
if (p[x] == x) return x;
int temp = p[x]; //当前节点不是根节点, 进行路径压缩以及维护d数组操作
p[x] = find(p[x]); //这里p[x]其实返回的是根节点, 所以上面要用temp临时存储p[x]
d[x] += d[temp]; //这样d[x]就增加的是当前这个节点到他原本上个父节点的距离
return p[x];
}
int main(void)
{
int n, k; scanf("%d %d", &n, &k);
for (int i = 1; i <= n; ++i) p[i] = i; //并查集初始化
int t, x, y; int res = 0;
while (k--) {
scanf("%d %d %d", &t, &x, &y);
if (x > n || y > n) { res++; continue; } //不符合2, 至于3和1可以合并判断
int px = find(x), py = find(y); //得到x与y的根节点
if (t == 1) {
if (px == py && (d[x] - d[y]) % 3) res++; //在一个集合, 且不是同类
else if (px != py) { //不在一个集合, 则建立关系
p[px] = py;
d[px] = d[y] - d[x]; //要满足 d[x] + X == d[y]
}
}
else {
if (px == py && (d[x] - d[y] - 1) % 3) res++; //在一个集合, x不是被y吃
else if (px != py) { //不在一个集合, 建立关系
p[px] = py;
d[px] = d[y] + 1 - d[x]; //要满足 d[x] - d[y] + X == 1
}
}
}
printf("%d\n", res);
return 0;
}