POJ - 1182 食物链(种类并查集)
思路
种类并查集和带权并查集都可以写。
种类并查集的写方法是,首先将长度N
扩展成3 * n
。
将 1
- N
设置为X
同类,N + 1
- 2 * N
设置为x
可以吃, 2 * N + 1
- 3 * N
设置为X
的天敌。
若D = 1,则表示X和Y是同类。我们就查询X与Y是否是天敌或可以吃的关系。
若D = 2,则表示X吃Y。我们就查询Y是否可以吃X 或者 X和Y是否是同类就行。
参考代码:
#include <iostream>
#define endl "\n"
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
int fa[maxn];
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x), y = find(y);
if (x != y) fa[x] = y;
}
void solve()
{
int n, k, ans = 0;
scanf("%d %d", &n, &k);
for (int i = 1; i <= 3 * n; i++) fa[i] = i;
// 1 -- n x同类
// n + 1 -- 2 * n x可以吃
// 2 * n + 1 -- 3 * n x天敌
while (k--)
{
int op, x, y;
scanf("%d %d %d", &op, &x, &y);
if (x > n || y > n) { ans++; continue; }
if (op == 1){
if (find(x) == find(y + n) || find(x) == find(y + 2 * n)){ans++; continue;}
else merge(x, y), merge(x + n, y + n), merge(x + 2 * n, y + 2 * n);
}
else{
if (x == y) { ans++; continue; }
else if (find(x + 2 * n) == find(y) || find(x) == find(y)) {ans++; continue;}
else merge(y, x + n), merge(x, y + 2 * n);
}
}
printf("%d\n", ans);
}
signed main()
{
int t = 1;
while (t--)
{
solve();
}
return 0;
}