本题可用并查集来解题。
假信息有如下几种情况:
(1)数据范围错误:
即:如果输入的动物编号小于0或者大于n,这显然是不符合题目条件的
(2)前后矛盾(这里认为最前面的是对的,而后面添加的与前面矛盾的信息是错误的)
即:如果x与y同类但x吃y或者y吃x,则矛盾!如果x吃y但是y又吃x,矛盾!
分析:
动物一共有三种,不妨设为A, B, C. 其中,A吃B, B吃C, C吃A
那么,1 x y 即:x吃y有三种可能性:x为A, y为B; x为B, y为C; x为C, y为A
同理,2 x y即:x与y是同类也有三种情况:x与y均为A; x与y均为B; x与y均为C
那么,我们就将这些情况全部枚举,并加入集合中。
这里要开一个3 * N的数组,其中[0, N - 1]记录A类,[N, 2N - 1]记录B类,[2N, 3N-1]记录C类
这里要注意的是,数组一定要开大!过小的话,虽然算法是对的,但是测试数据过大时会爆数组。
C++代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX_N = 3 * 50010;
const int MAX_K = 100010;
int par[MAX_N]; // 父亲
int Rank[MAX_N]; // 树的高度
int T[MAX_K], X[MAX_K], Y[MAX_K];
int N, K;
// 初始化n个元素
void init(int n) // 一定不要忘了初始化!!!
{
for (int i = 0; i < n; i++)
{
par[i] = i; // 初始化i的父亲:一开始,每个节点都是自己的祖先
Rank[i] = 0; // 初始化i的高度,一开始,每个节点都是自己的祖先即树根,而树根的高度为0
}
}
// 查询并返回树的根
int find(int x)
{
if (par[x] == x)
return x;
else
return par[x] = find(par[x]); // return a = b; 的意思是,将b的值赋值给a,然后返回a的值
// 这里的意思是将x的父亲的父亲赋值给x的父亲,也就是已知沿着树干向上找根节点
}
// 合并x和y所在的集合
void unite(int x, int y)
{
x = find(x); y = find(y); // 将x和y的根分别赋值给x和y
if (x == y) return; // 如果x于y的根相同,则不必合并
if (Rank[x] < Rank[y]) /// 合并时如果两棵树的高度不同,则将Rank小的连到Rank到的上面
par[x] = y;
else
{
par[y] = x;
if (Rank[x] == Rank[y]) Rank[x]++; // 如果相同,则最高的高度合并之后需要在原来的基础上+1
}
}
bool same(int x, int y) // 判断x和y是否属于同一个集合
{
return find(x) == find(y); // 只需判断x和y的根节点是否相同
}
void solve()
{
init(N * 3);
int ans = 0;
for (int i = 0; i < K; i++)
{
int t = T[i];
int x = X[i] - 1, y = Y[i] - 1; // 这样就能够出现编号为0的编号了,并且没有编号为1的编号了
if (x < 0 || x >= N || y < 0 || y >= N) // 如果x或者y的编号越界
{
ans++;
continue;
}
if (t == 1) // 如果1 x y即:本条给定的信息是:x与y是同类
{
if (same(x, y + N) || same(x, y + 2 * N)) // 如果x吃y并且y也吃x,矛盾!说明此条信息有误
ans++;
else // 否则,x与y为同类的动物,则合并他们
{
unite(x, y);
unite(x + N, y + N);
unite(x + 2 * N, y + 2 * N);
}
}
else // 2 x y 代表x吃y
{
if (same(x, y) || same(x, y + 2 * N)) // 如果x与y是同类或者y吃x,又矛盾,说明此条信息有误
ans++;
else // 否则,x可以吃y,则将这条关系添加到集合中
{
unite(x, y + N);
unite(x + N, y + 2 * N);
unite(x + 2 * N, y);
}
}
}
printf ("%d\n", ans);
}
int main()
{
scanf ("%d%d", &N, &K);
for (int i = 0; i < K; i++)
scanf ("%d%d%d", &T[i], &X[i], &Y[i]);
solve();
return 0;
}