P2024 [NOI2001] 食物链 解题报告

题意

有三类动物 \(A,B,C\),这三类动物的食物链构成了环形。\(A\)\(B\)\(B\)\(C\)\(C\)\(A\)

每个动物都是 \(A,B,C\) 中的一种,我们并不知道它到底是哪一种。

有两种指令

  • 1 X Y,表示 \(x\)\(y\) 是同类。
  • 2 X Y,表示 \(x\)\(y\)

一个指令被定义为假指令的条件为:

  • 与前面的某一条指令冲突。
  • \(x\)\(y\)\(n\) 大。

\(k\) 个指令中有多少假指令

对于全部数据,\(1\le n\le 5 \times 10^4\)\(1\le k \le 10^5\)\(\lvert x \rvert, \lvert y \rvert < 2^{32}\)

思路

考虑没有假指令时是怎么做的。

可以直接想到用并查集合并。

考虑将并查集分为 \(3\) 个类群,初始每个动物在三个类群里。

然后就很好做了:

  • 判断假话
    • \(x\)\(y\) 是同类的条件是 \(x\)\(y\) 不互相吃。
    • \(x\)\(y\) 的条件是 \(x\)\(y\) 不是同类
  • 如果是真话
    • 如果 \(x\)\(y\) 是同类,那么将每个同一类别的 \(x\)\(y\) 连在一起。
    • 如果 \(x\)\(y\) 那么将每一类型的 \(x\) 都和他可以吃的那个类型的 \(y\) 连在一起。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n, k, f[N], ans;
int op, u, v;

int find(int x)
{
    if(f[x] == x) return x;
    return f[x] = find(f[x]);
}

void init(int n)
{
	for(int i = 1; i <= n * 3; i ++)
    {
        f[i] = i;
    }
}

signed main()
{
    cin >> n >> k;
    init(n);
    for(int i = 1; i <= k; i ++)
    {
    	scanf("%d %d %d", &op, &u, &v);
        if(u > n || v > n)
        {
            ans ++;
            continue;
        }
        if(op == 1)
        {
            if(find(u + n) == find(v) || find(u) == find(v + n))
            {
                ans ++;
            }
            else
            {
                f[find(u)] = find(v);
                f[find(u + n)] = find(v + n);
                f[find(u + n + n)] = find(v + n + n);
            }
        }
        else
        {
            if(find(u) == find(v) || find(u) == find(v + n))
            {
                ans ++;
            }
            else
			{
                f[find(u + n)] = find(v);
                f[find(u + n + n)] = find(v + n);
                f[find(u)] = find(v + n + n);
            }
        }
    }
    cout << ans << endl;
}
posted @ 2026-06-04 14:38  Luowj  阅读(12)  评论(0)    收藏  举报