洛谷 P2024 食物链
题目传送门:https://www.luogu.com.cn/problem/P2024
一般的并查集只能用来解决”亲戚的亲戚是亲戚“这类问题,但是如果关系变得复杂就不好解决。
比如”敌人的敌人是朋友“这类的问题,很明显普通的并查集无法解决,这个时候我们就要用到普通并查集的进阶版——种类并查集。
一般的种类并查集就是将数据范围扩大1倍,开一个2*n的数组,并将其分为两段,一段用来存放朋友,一段用来存放敌人。
本题存在三种关系,同类,猎物,天敌。所以仅仅开2*n的数组肯定不够,所以我们可以尝试开一个3*n的数组。
对于一个数x来说,x为自己,x+n为猎物,x+2*n为天敌。用1 ~ n来存同类,用n+1 ~ 2*n来存猎物,用2*n+1 ~ 3*n来存天敌。
注意:如果对于x+n来说,x+n为自己,x+2*n为猎物,x为天敌。以此类推。
我们还要从题目中获取隐藏信息:
1、如x和y为同类,那么x+n和y+n为同类,x+2*n和y+2*n为同类。
2、如x吃y,那么x+n吃y+n,x+2*n吃y+2*n。
3、如x吃y,y吃z,那么z吃x。
了解了以上信息,ac本题就相当容易。
下面为ac代码:
#include <bits/stdc++.h>
using namespace std;
int fa[150010];
//普通并查集的板子
int find_x(int x)
{
if(x==fa[x]) return x;
fa[x]=find_x(fa[x]);
return fa[x];
}
void add(int x,int y)
{
fa[find_x(x)]=find_x(y);
}
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=3*n;i++) //初始化
fa[i]=i;
int sum=0;
while(q--)
{
int s,x,y;
cin>>s>>x>>y;
if(x>n || y>n) //不在范围内
{
sum++;
continue;
}
if(s==1) //情况一
{
if(find_x(x+n)==find_x(y) || find_x(x+2*n)==find_x(y)) //如果是捕食或被捕食的关系就是假话
{
sum++;
continue;
}
add(x,y),add(x+n,y+n),add(x+2*n,y+2*n); //如x和y为同类,那么x+n和y+n为同类,x+2*n和y+2*n为同类
}
if(s==2) //情况二
{
if(x==y) //不可能自己捕食自己
{
sum++;
continue;
}
if(find_x(x)==find_x(y) || find_x(x+2*n)==find_x(y)) //如果是同类或被捕食的关系就是假话
{
sum++;
continue;
}
add(x+n,y),add(x+2*n,y+n),add(x,y+2*n); //如x吃y,那么x+n吃y+n,x+2*n吃y+2*n
}
}
cout<<sum;
return 0;
}

浙公网安备 33010602011771号