【Ybtoj 第10章例4】食物链【并查集】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解题思路

唉一开始理解错题意,打了一个裸的并查集。。。我太菜了啊啊啊啊啊

巨爷们说这道题好像有两种方法:

算法一:扩展域并查集

引入一个新的并查集拓展:扩展域并查集。

这种做法用到了图论中的经典思想:拆点,即当图中每一个结点需要表示多个信息时,将每个节点拆成多个点,拆出的每个点分别表示原点的一部分信息。

回到本题,我们考虑将动物按吃与被吃的关系分类:以 x x x动物为例,引入虚拟动物 x + n x+n x+n表示 x x x吃的动物,引入虚拟动物 x + 2 ∗ n x+2*n x+2n表示被 x x x吃的动物,将同种动物放到同一个集合中,可以用并查集维护。

合并并查集的操作方法如下:
当给出的x和y同类时,合并 x x x y y y所在的集合,合并 x + n x+n x+n y + n y+n y+n所在的集合,合并 x + 2 ∗ n x+2*n x+2n y + 2 ∗ n y+2*n y+2n所在的集合.

判断假话:以给出的话表示 x x x y y y是同类为例,若之前得出 x x x y + n y+n y+n(即 x x x和吃 y y y的动物是同类)或, x x x y + 2 ∗ n y+2*n y+2n(即x和被y吃的动物是同类)显然都可以判定为假话。 x x x y y y的情况同理。

这种算法的优点很显然降低了思维难度和代码难度,但其付出的代价是更多的空间和更大的时间复杂度常数,运用范围没有带权并查集广泛。

算法二:带权并查集

弄一个很复杂的边权之类的东西,然后还要取模,套一堆式子。所以,由于我太懒了 哈哈 就没有打这种方法。(但我找了一篇非常好的博客,弥补我的罪行:解法二

PS:注意要先特判掉 x x x y y y大于 n n n x x x等于 y y y的情况


代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<bitset>
using namespace std;

int n,k,fa[150010],w,x,y,ans;

int find(int x){
    
    
	if(fa[x]!=x)
		return fa[x]=find(fa[x]);
	else return fa[x];
}

int main(){
    
    
	scanf("%d%d",&n,&k);
	for(int i=1;i<=3*n;i++)//i+n表示天敌,i+2*n表示猎物 
		fa[i]=i;
	while(k--)
	{
    
    
		scanf("%d%d%d",&w,&x,&y);
		if(x>n||y>n)
		{
    
    
			ans++;
			continue;
		}
		int xx=find(x),yy=find(y),sx=find(x+n),sy=find(y+n),tx=find(x+2*n),ty=find(y+2*n);
//xx,yy表示x和y的同类,sx,sy表示x和y的天敌,tx,ty表示x和y的猎物 
		if(w==1)
		{
    
    
			if(xx==sy||xx==ty){
    
    //说是同类,却是天敌或猎物,就是假
				ans++;
				continue;
			}
			else fa[xx]=yy,fa[sx]=sy,fa[tx]=ty;
		}
		if(w==2)
		{
    
    
			if(xx==yy||sx==yy)//说是x吃y,却是同类或y吃x,就是假
			{
    
    
				ans++;
				continue;	
			}
			else fa[xx]=sy,fa[sx]=ty,fa[tx]=yy;//x的同类是y的天敌,x的天敌是y的猎物,x的猎物是y的同类 
		}	
	}
	printf("%d",ans); 
}

猜你喜欢

转载自blog.csdn.net/kejin2019/article/details/115211807
今日推荐