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 1

1.如果两个动物的父节点都是自己,说明这句话是真的,两只动物还没有关系,所以我们直接合并

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;
}
posted @ 2023-01-08 22:48  Zeoy_kkk  阅读(32)  评论(0编辑  收藏  举报