P2024 食物链 - 洛谷
动物王国中有三类动物A
,B
,C
,这三类动物的食物链构成了有趣的环形。A
吃B
,B
吃C
,C
吃A
。
现有N
个动物,以1
- 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
句话,输出假话的总数。
第一行两个整数N
,K
,表示有N
个动物,K
句话。
第二行开始每行一句话(按照题目要求,见样例)
一行,一个整数,表示假话的总数。
输入 | 输出 |
---|---|
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5 |
3 |
1≤N≤5×1E4
, 1≤K≤1E5
A
、B
、C
三个集合分不同空间存放,对于每次询问判断其真实性;如果询问为真,那么在3个集合中建立关系,重复操作。
参考:题解 P2024 【食物链】 - Sooke’s Blog - 洛谷博客
对于动物 x 和 y,我们可能有 x 吃 y, x 与 y 同类, x 被 y 吃。
······
我们将并查集分为 3 个部分,每个部分代表着一种动物种类。
设我们有 n 个动物,开了 3n 大小的种类并查集,其中 1∼n 的部分为 AA 群系, n+1∼2n 的部分为 B 群系, 2n+1∼3n 的部分为 C 群系。
······
当 A 中的 x 与 B 中的 y 合并,有关系 x 吃 y;当 C 中的 x 和 C 中的 y 合并,有关系 x 和 y 同类等等……
但仍然注意了!我们不知道某个动物属于 A, B,还是 C,我们 3 个种类都要试试!
也就是说,每当有 1 句真话时,我们需要合并 3 组元素。
容易忽略的是,题目中指出若 x 吃 y, y 吃 z,应有 x 被 z 吃。
这个关系还能用种类并查集维护吗?答案是可以的。
若将 x 看作属于 A,则 y 属于 B, z 属于 C。最后,根据关系 A 被 C 吃可得 x 被 z 吃。
······
对于样例的图片解释:
用的并查集类和一般的并查集是一样的
[模板] 并查集 - 一般并查集 (洛谷 P3367 并查集)
//c++
#include
struct ufsets_elem{ //并查集元素
ufsets_elem *root;
ufsets_elem(){
root = this;
}
ufsets_elem *find(){
return root == this ? root : root = root->find(); //路径压缩
}
};
struct ufsets{ //并查集
protected:
int ufsets_num; //独立集合数量
ufsets_elem *ufsets_base; //并查集数组
public:
explicit ufsets(const int &n){
ufsets_base = new ufsets_elem[n + 1]();
ufsets_num = n;
}
bool catenate(const int &a, const int &b){ //合并a,b集合,返回false则说明两元素已是同一集合
if (ufsets_base[a].find() != ufsets_base[b].find()){ //若a,b不在同一集合则合并b至a
--ufsets_num;
ufsets_base[b].root->root = ufsets_base[a].root;
return true;
}
return false;
}
bool relative(const int &a, const int &b){ //查询是否在同一个集合中,是则返回true
return ufsets_base[a].find() == ufsets_base[b].find();
};
int size(){//独立集合数量
return ufsets_num;
}
~ufsets(){
delete[] ufsets_base;
}
};
int main(){
int n, k, r = 0;
int tmp1, tmp2, tmp3;
scanf("%d%d", &n, &k);
ufsets u(n * 3);
for (int i = 1; i <= k; i++){
scanf("%d%d%d", &tmp1, &tmp2, &tmp3);
if (tmp2 > n || tmp3 > n){
r++;
continue;
}
switch (tmp1){
case 1: //同类关系
if (u.relative(tmp2 + n, tmp3) || u.relative(tmp2 + n * 2, tmp3)) //假话
r++;
else //真话
u.catenate(tmp2, tmp3), u.catenate(tmp2 + n, tmp3 + n), u.catenate(tmp2 + n * 2, tmp3 + n * 2);
break;
case 2: //捕食关系
if (u.relative(tmp2, tmp3) || u.relative(tmp2 + n * 2, tmp3)) //假话
r++;
else //真话
u.catenate(tmp2, tmp3 + n * 2), u.catenate(tmp2 + n, tmp3), u.catenate(tmp2 + n * 2, tmp3 + n);
break;
}
}
printf("%d\n", r);
return 0;
}
//java
import java.util.*;
import java.math.*;
class ufsets_elem{ //并查集元素
public ufsets_elem root;
ufsets_elem(){
root = this;
}
ufsets_elem find(){
return root == this ? root : (root = root.find()); //路径压缩
}
};
class ufsets{ //并查集,依赖于class ufsets_elem
int ufsets_num; //独立集合数量
ufsets_elem[] ufsets_base; //并查集数组
ufsets(final int n){
ufsets_base = new ufsets_elem[n + 1];
for(int i=1;i<=n;i++)//ufsets_base[0]用不到
ufsets_base[i]=new ufsets_elem();
ufsets_num = n;
}
boolean catenate(final int a, final int b){ //合并a,b集合,返回false则说明两元素已是同一集合
if (ufsets_base[a].find() != ufsets_base[b].find()){ //若a,b不在同一集合则合并b至a
--ufsets_num;
ufsets_base[b].root.root = ufsets_base[a].root;//b更改的是其父节点的
return true;
}
return false;
}
boolean relative(final int a, final int b){ //查询是否在同一个集合中,是则返回true
return ufsets_base[a].find() == ufsets_base[b].find();
};
int size(){//独立集合数量
return ufsets_num;
}
};
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(), k=sc.nextInt(),r=0;
int tmp1, tmp2, tmp3;
ufsets u=new ufsets(n * 3);
for (int i = 1; i <= k; i++){
tmp1=sc.nextInt();
tmp2=sc.nextInt();
tmp3=sc.nextInt();
if (tmp2 > n || tmp3 > n){
r++;
continue;
}
switch (tmp1){
case 1: //同类关系
if (u.relative(tmp2 + n, tmp3) || u.relative(tmp2 + n * 2, tmp3)) //假话
r++;
else { //真话
u.catenate(tmp2, tmp3);
u.catenate(tmp2 + n, tmp3 + n);
u.catenate(tmp2 + n * 2, tmp3 + n * 2);
}
break;
case 2: //捕食关系
if (u.relative(tmp2, tmp3) || u.relative(tmp2 + n * 2, tmp3)) //假话
r++;
else { //真话
u.catenate(tmp2, tmp3 + n * 2);
u.catenate(tmp2 + n, tmp3);
u.catenate(tmp2 + n * 2, tmp3 + n);
}
break;
}
}
System.out.println(r);
}
}