洛谷 P2024 拓展域并查集

题目在这里

题意

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

现有 \(N\) 个动物,以 \(1 \sim N\) 编号。每个动物都是 \(A,B,C\) 中的一种,但是我们并不知道它到底是哪一种。

有人用两种说法对这 \(N\) 个动物所构成的食物链关系进行描述:

  • 第一种说法是 \(1\) \(X\) \(Y\),表示 \(X\)\(Y\) 是同类。
  • 第二种说法是 \(2\) \(X\) \(Y\),表示 \(X\)\(Y\)

此人对 \(N\) 个动物,用上述两种说法,一句接一句地说出 \(K\) 句话,这 \(K\) 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  • 当前的话与前面的某些真的话冲突,就是假话;
  • 当前的话中 \(X\)\(Y\)\(N\) 大,就是假话;
  • 当前的话表示 \(X\)\(X\),就是假话。

你的任务是根据给定的 \(N\)\(K\) 句话,输出假话的总数。

分析

用拓展域并查集, \([i,n]\)表示同类, \([i+n, 2 * n]\) 表示猎物, \([i + 2 * n, i + 3 * n]\) 表示天敌
猎物的猎物是天敌,天敌的天敌是猎物

  • 对于查询\(1\)如果 \(X\) \(Y\) 互吃则为假
  • 对于查询\(2\)如果 \(X\) \(Y\) 是同类或 \(Y\)\(X\) 则为假

代码

点击查看代码
#include <bits/stdc++.h>

using namespace std;
const int MAXN = 200010;
int n, k, fa[MAXN], ans = 0;
// i~n 同类, i + n ~ 2 * n 猎物, i + 2 * n ~ i + 3 * n 天敌 
int find(int x)
{
	if(fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> k;
	for(int i = 1; i <= 3 * n; i++) fa[i] = i;
	for(int i = 0; i < k; i++)
	{
		int op, x, y;
		cin >> op >> x >> y;
		if(x > n || y > n) ans++;
		else if(op == 1)
		{
			// 如果x, y不互吃 
			if(find(x) != find(y + n) && find(x) != find(y + 2 * n) && find(x + n) != find(y) && find(x + 2 * n) != find(y)) 
			{
				fa[find(y)] = find(x); // x 与 y 合并 
				fa[find(y + n)] = find(x + n); // x的猎物与y的猎物合并 
				fa[find(y + 2 * n)] = find(x + 2 * n); // y的天敌与x的天敌合并 
			}
			else ans++;
		}
		else if(op == 2)
		{
			// 如果 y 吃 x 
			if(find(x) == find(y) || find(x) == find(y + n) ||find(x + 2 * n) == find(y)) ans++;
			else 
			{
				fa[find(y + 2 * n)] = find(x); // y 的天敌与 x合并 
				fa[find(x + n)] = find(y); // x 的猎物与 y合并 
				fa[find(x + 2 * n)] = find(y + n); // x的天敌与 y的猎物合并 
			}
		}
	}printf("%d", ans);
	return 0;
}
posted @ 2025-08-01 18:30  东东哥本人  阅读(8)  评论(0)    收藏  举报