点击查看题目<题目:食物链>
我也是看一些博客才慢慢会写的, 我现在对这题解的感受是: 我能写出来, 也能理解一些, 但感觉还是不能充分理解原理, 有种似懂非懂的感受
题解大意是将A,B,C类分成三个区间, 1~n 是A类, n~2*n 是B类, 2*n~3*n 是C类, 所以一开始要初始化3*n个数据, 至于原理, 我还不能够说出来
判断假话有三种情况
第一种情况: x>n 或 y>n ,这很简单, 直接判断就行
第二种情况: d==1 , 即x,y是同类
假如这时候d==1, 但却x,y 不是同类, 不就是假话了!
也就是---> x在 A类(1~n), b却在 B类(n~2*n) , 代码中解释就是 find(x) == find(y+n) x属于[1,n] A类, y+n属于[ n~2*n] B类
或者 x在 A类(1~n), b却在 C类(2*n~3*n), 代码中解释就是 find(x+n) == find(y+2*n)
第三种情况: d==2 , 即x吃y, 题目中说了A类能吃B类, B类能吃C类, C类能吃A类
这时候如果 x不能吃y 不就是假话了!
怎样x才不会吃y呢? 只有这两种情况
第一: x和y是同类,代码中解释就是 find(x) == find(y)
第二: x是A类, y是C类, 题目中可没说A类能吃C类!!!, 代码中解释就是 find(x) == find(y+2*n)
如果不是假话, 那就结合在一类吧, 代码中有注释, 方便理解
参考文章 https://blog.csdn.net/u014004096/article/details/44525255
代码 : 注意尽量将cin改写成scanf, 不然可能会超时
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = (int)5e4 * 3 + 5;
int per[MAX];
//前两个函数都是套路
int find(int x)
{
int r = x;
while (r != per[r])
r = per[r];
int i = x, j;
while (i != r)
{
j = per[i];
per[i] = r;
i = j;
}
return r;
}
void join(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)
per[fx] = fy;
}
int main()
{
int n, m,res=0;
cin >> n >> m;
for (int i = 1; i <= 3 * n; ++i)
per[i] = i;
while (m--)
{
int c, a, b;
scanf("%d%d%d", &c, &a, &b);
if (a > n || b > n)
{
res++; //假话
continue;
}
if (c == 1)//同类
{
//如果不是同类 两种情况---> a在A类,b在B类 或者 a在A类,b在C类 (没必要再考虑b在A类, a在B类等情况)
if (find(a) == find(b + n) || find(a) == find(b + 2 * n))
{
res++;
continue; //假话,没必要往下执行
}
else// 如果是同类
{
//有三种可能
join(a, b); // a,b都在A类
join(a + n, b + n);// a,b都在B类
join(a + 2 * n, b + 2 * n);// a,b 都在C类
}
}
else if (c == 2)//a吃b
{
//如果a,b同类 或者 a在A类,b在C类,因为A类不能吃C类,不满足猎食关系
if (find(a) == find(b) || find(a) == find(b + 2 * n))
{
res++; //假话
continue;
}
else
{
//三种情况
join(a, b + n);// a在A类, b在B类
join(a + n, b + 2 * n);// a在B类, b在C类
join(a + 2 * n, b); // a在C类, b在A类
}
}
}
cout << res << endl;
return 0;
}