POJ - 1182 食物链
POJ - 1182 食物链
题解:种族并查集
引理:对于普通的并查集,我们总是用来查找和维护每个元素之间的同类关系,而种族并查集总是用来解决一些存在对立关系,而且对象的关系存在传递性和循环性,比如食物链:A->B,B->C,C->A,或者说敌人的敌人是朋友,朋友的朋友是朋友这些关系,我们可以开多个补集来存放他们的对立面
回到题目:首先关系有三种,同类,食物,敌人,所以我们可以开三倍的并查集,\([1,n]\)代表同类,\([n+1,2n]\)代表食物,\([2n+1,3n]\)代表敌人;那么对于\([n+1,2n]\)来说,\([2n+1,3n]\)代表食物,\([1,n]\)代表敌人,另一种情况不再赘述,下面我们来看一个样例
4 5
1 1 3
2 2 4
2 3 2
1 1 4
2 2 11.如果两个动物的父节点都是自己,说明这句话是真的,两只动物还没有关系,所以我们直接合并
2.怎么判断x吃y,我们只要判断x和y不是同类并且y不吃x,或者说x只要在他的食物关系中找到y(意思是两个人在同一个集合,根一样),就能说明x吃y
3.怎么判断x和y是同类,我么只要判断x不吃y并且y不吃x,所以x在他的食物关系中找不到y,y在他的食物关系中也找不到x,就说明x和y是同类
z#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <string.h>
#include <ctype.h>
#include <vector>
#include <math.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define endl '\n'
using namespace std;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 5e4 + 10;
int fa[3 * N];
int n, m;
void init()
{
for (int i = 1; i <= 3 * n; ++i)
fa[i] = i;
}
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
int fx = find(x);
int fy = find(y);
fa[fx] = fy;
}
int main(void)
{
Zeoy;
int t = 1;
// cin >> t;
while (t--)
{
int k;
cin >> n >> k;
init();
int ans = 0;
for (int i = 1; i <= k; ++i)
{
int op, x, y;
cin >> op >> x >> y;
if (x > n || y > n) // x或y比n大,题目规定是假话
{
ans++;
continue;
}
if (op == 1) // 判断是否是同类
{
if (find(x) == find(y + n) || find(y) == find(x + n)) //如果x吃y或者y吃x
ans++; // 代表不是同类,是假话
else
{
merge(x, y);
merge(x + n, y + n);
merge(x + 2 * n, y + 2 * n);
}
}
else if (op == 2) // 判断x是否吃y
{
if (x == y)
{
ans++; // 表示X吃X,题目规定是假话
continue;
}
if (find(x) == find(y) || find(y) == find(x + n)) //x和y是同类,或者y吃x都说明是假话
ans++;
else
{
merge(x, y + n);
merge(x + n, y + 2 * n);
merge(x + 2 * n, y);
}
}
}
cout << ans << endl;
}
return 0;
}