当前位置: 首页 > news >正文

【种类并查集】洛谷 P2024 [NOI2001] 食物链

View Post

【种类并查集】洛谷 P2024 [NOI2001] 食物链

题目

https://www.luogu.com.cn/problem/P2024

题解

种类并查集又称为扩展域并查集,是并查集的一种扩展形式,主要用于处理多组对立关系或多种状态划分的问题。在处理种类并查集问题时,有多少种类,就得开几倍的空间,用于表示所有的种类。

由题意,不妨假设现在有 \(n\) 种动物,且这 \(n\) 种动物必定都是 \(A,B,C\) 其中一种。它们的关系是 \(A\)\(B\)\(B\)\(C\)\(C\)\(A\)。那么对于该题,就是有三种种类,分别是同类、猎物和天敌。因此,使用种类并查集就需要开 3 倍空间大小。对于第 \(x\) 个人,不妨设其同类的集合为 \(exdsu[x]\),猎物的集合为 \(exdsu[x+n]\),天敌的集合为 \(exdsu[x+2 \times n]\)。可以画出其关系图为:
食物链1

不妨假设存在两只动物 \(a(1 \leq a \leq n)\)\(b(1 \leq b \leq n)\),那么 \(a\) 的同类、猎物和天敌的集合分别为 \(exdsu[a],exdsu[a+n] 和 exdsu[a+2 \times n]\)\(b\) 的同类、猎物和天敌的集合分别为 \(exdsu[b],exdsu[b+n] 和 exdsu[b+2 \times n]\)。可以画出它们的关系图为:
食物链2

\(b\)\(a\) 的猎物,则 \(exdsu[a+n]\)\(exdsu[b]\) 是同类,那么有关系图(为方便画图,简单将 \(exdsu[x]\) 表示为 \(x\)):
食物链3

因为猎物关系和天敌关系不好表示,因此可以将它们转化为同类关系,使用种类并查集将所有的同类关系的集合合并起来。那么根据上述图表关系,有:

  • \(x\)\(y\) 的同类,则需要分别将 \(exdsu[x]与exdsu[y],exdsu[x+n]与exdsu[y+n]和exdsu[x+2 \times n] 与exdsu[y+ 2 \times n]\) 进行合并;
  • \(y\)\(x\) 的猎物,则需要分别将 \(exdsu[x]与exdsu[y+2 \times n],exdsu[x+n]与exdsu[y]和exdsu[x+2 \times n] 与exdsu[y+n]\) 进行合并。

参考代码

#include<bits/stdc++.h>
using namespace std;constexpr int N = 5e4 + 1;
int n, k, x, y, ans;
char op;
int exdsu[N * 3];int find(int x) {return x == exdsu[x] ? x : exdsu[x] = find(exdsu[x]);
}void merge(int x, int y) {int fx = find(x), fy = find(y);exdsu[fy] = fx;
}bool isLie() {if (x > n || y > n) return true;// x 或 y 比 n 大必是假话if (op == '2' && x == y) return true;// 吃自己是假话if (op == '1' && (find(x) == find(y + n) || find(x + n) == find(y))) return true;// 已经是异类却说是同类,假话if (op == '2' && (find(x) == find(y) || find(y + n) == find(x))) return true;// x 是 y 的同类或猎物return false;// 说的是真话
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin >> n >> k;iota(exdsu + 1, exdsu + 1 + 3 * n, 1);while (k --) {cin >> op >> x >> y;if (isLie()) {++ ans;continue;}if (op == '1') {merge(x, y);merge(x + n, y + n);merge(x + 2 * n, y + 2 * n);} else {merge(x + n, y);merge(x + 2 * n, y + n);merge(x, y + 2 * n);}}cout << ans << '\n';return 0;
}