食物链 POJ - 1182

动物王国中有三类动物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句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

1) 当前的话与前面的某些真的话冲突,就是假话;

2) 当前的话中X或Y比N大,就是假话;

3) 当前的话表示X吃X,就是假话。

你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input
第一行是两个整数N和K,以一个空格分隔。 以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3

这个题有点毒,说句实话真的好难,在网上找个好久的题解,这个题又两种题解,第一种的话是带权并查集不过路径压缩算法公式和合并算法公式要自己推,我只看懂了第一个路径压缩算法公式推导,第二个实在是神一般的操作。然后看了一下第二种解法,我也是一脸懵逼,不过我看了好久,终于大概知道它的思路了:
题目不是输入的d要么是1要么是2,代表要么是同类,要么x吃y,然后它就是根据这个关系来维护并查集,不过和我们的思维有点不一样,我们一般会维护这由x,y,z组成的n个节点,然后大佬的思维就是将这n个节点扩展成3n个节点,然后维护它,一开始我是不理解为什么要扩展成3n个,为什么不是2n,4n个,因为题目只给出3类动物,所以在查询的时候,3n就可以覆盖所有节点了,(就是可以将三个种类并起来,如果是n的话并起来可以,但是就没有种类的那种关系了),然后有了三个种类,我们就可以确定关系了,题目给出的两种关系,同类或者是x吃y的关系,嗯。。。。我们可以这样,因为有了这样一个3n的空间我们可以自己定义同类关系,我们将 parent[x]==y,parent[x+n]==y+n,parent[x+2n]= y+2n, 这样一个一一对应关系定义为同类关系,然后我们可以再定义一个x吃y的关系,parent[x]=y+n,parent[x+n]=y+2n,parent[x+n*2]=y;

然后定义好了这两个关系后,我把程序的思路整理一下:
输入 n,k;(n是节点数,也就是三种类型动物的编号,k是k句询问)

for i=0 to k
输入 d,x,y;//x吃y
if (x>n||y>n||(d<mark>2&&x</mark>y))ans++;这是另外两种假话的情况这个比较简单;
if(d==1)
考虑这句话为假话的情况只有在已有的关系中,x吃y,或者y吃x,那么他们一定不是同类,那么这句话就是假话,记住是在已经存在的关系中判断,一开始的话默认是对的,因为这个关系图还没有建好,题目假设前面的话是对的来判断后面的话。
否则就是关系还没建立,或者这句话是真话,那么我们建立关系图。
parent[x]==y,parent[x+n]<mark>y+n,parent[x+2n]==y+2n,将这些点合并就行了。
if(d</mark>2)
考虑这句话为假话的情况只有:x和y是同类或者y吃x,否则这句话是真话,我们建图。
第二种方法我还是有点懵再花点时间看一下。。。
ac代码:

#include<iostream>
using namespace std;
const int maxn=50000+10;

int parent[maxn*3];

int find_x(int x){
	if(x!=parent[x]){
		parent[x]=find_x(parent[x]);
	}
	return parent[x];
}

void ufs(int x,int y){
	int a=find_x(x);
	int b=find_x(y);
	if(a!=b){
		parent[a]=b;
	}
}

int main()
{
	int ans=0;
	int n,k;
	int d,x,y;
	scanf("%d %d",&n,&k);
	for(int i=1;i<=3*n;i++){
		parent[i]=i;
	} 
	for(int i=0;i<k;i++){
		scanf("%d %d %d",&d,&x,&y);
		if((x>n||y>n)||(d==2&&x==y)){
			ans++;
		}else if(d==1){
			if((find_x(y)==find_x(x+n))||(find_x(x)==find_x(y+n)))//如果y吃x,或者x吃y,则说明当前说的是假话 
			ans++;
			else{
				ufs(x,y);
				ufs(x+n,y+n);
				ufs(x+2*n,y+2*n);
			}
		}else if(d==2){
			//x吃y 
			if((find_x(x)==find_x(y))||(find_x(x)==find_x(y+2*n))){//x与y是同种类型或者y吃x,说明当前说的是假话 
				ans++; 
			}else{
				ufs(x,y+n);
				ufs(x+n,y+2*n);
				ufs(x+2*n,y);
			}
		}
	}
	printf("%d\n",ans);
}

参考文献:
https://blog.csdn.net/weixin_42488861/article/details/97621286
https://blog.csdn.net/niushuai666/article/details/6981689
https://www.cnblogs.com/zhuanzhuruyi/p/5863738.html