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;
}

浙公网安备 33010602011771号