有
两种陈述:
1 x y: 和 是同类;2 x y: 吃 。
需要判断每个陈述是否为真。若与前面已知的真话矛盾,则为假话。输出假话的总数。
题目
给定
假话判定规则:
- 当前陈述中
或 大于 ,为假话; 1 x y表示 和 同类,若已知 吃 或 吃 ,为假话;2 x y表示 吃 ,若已知 和 同类或 吃 ,为假话。
输入格式
- 第一行两个整数
和 ; - 接下来
行,每行三个整数d x y。
输出格式
- 输出一个整数,表示假话的数量。
样例
样例输入
input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5样例输出
output
3样例解释
1 101 1: ,假话(计数 1)2 1 2:真话,记录 吃2 2 3:真话,记录 吃2 3 3: ,一个动物不能吃自己,假话(计数 2)1 1 3:已知 吃 , 吃 ,根据传递性 吃 (即 和 不同类),假话(计数 3)2 3 1:真话,与前面一致( 被 吃,即 吃 )1 5 5: ,同类陈述为真
假话总数为
题解
点击查看题解
核心思路
使用扩展域并查集(或称带权并查集):
- 对于每个动物
,维护三个域:x(表示 本身)、x+n(表示 的猎物)、x+2n(表示 的天敌); 1 x y(同类):将x与y、x+n与y+n、x+2n与y+2n分别合并;2 x y( 吃 ):将x与y+n( 是 的天敌)、x+n与y+2n( 的猎物是 的天敌)、x+2n与y( 的天敌是 的同类)分别合并。
判断假话:在执行合并前,检查是否与已知关系矛盾。
复杂度分析
- 时间复杂度:
; - 空间复杂度:
。
点击查看参考代码
cpp
#include <iostream>
#include <vector>
using namespace std;
struct DSU {
vector<int> fa;
DSU(int n) {
fa.resize(n + 1);
for (int i = 0; i <= n; ++i) fa[i] = i;
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unite(int a, int b) {
a = find(a), b = find(b);
if (a != b) fa[a] = b;
}
bool same(int a, int b) {
return find(a) == find(b);
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
DSU dsu(3 * n);
int ans = 0;
while (k--) {
int d, x, y;
cin >> d >> x >> y;
if (x > n || y > n) { ans++; continue; }
if (d == 1) {
if (dsu.same(x, y + n) || dsu.same(x, y + 2 * n)) ans++;
else {
dsu.unite(x, y);
dsu.unite(x + n, y + n);
dsu.unite(x + 2 * n, y + 2 * n);
}
} else {
if (x == y || dsu.same(x, y) || dsu.same(x, y + 2 * n)) ans++;
else {
dsu.unite(x, y + n);
dsu.unite(x + n, y + 2 * n);
dsu.unite(x + 2 * n, y);
}
}
}
cout << ans << '\n';
return 0;
}本地运行与提交
powershell
pnpm lab:run -- labs/chapter-05/exercise/E-05-14-food-chain-dsu