食物链(最全的代码+解析)

	#include<bits/stdc++.h>
        #pra\
        gma GCC optimize(2)
	using namespace std;
	int f[100001],d[100001],p,x,y,n,m,ans=0;
	int findfat(int x)  
	{  
	    if(f[x]==x) return x;
		int fa=f[x];  
	    f[x]=findfat(f[x]); 
		d[x]=(d[x]+d[fa])%3;//食物链中关系分为0,1,2三种,其中0代表是同类,1代表被父节点吃,2代表吃父节点
		//则每个父节点和自己的关系均为0(自己当然不能吃自己的啦)。
		//它所连接的子节点(通过穷举法可进行证明)
		/*
		爷爷 父亲 儿子  爷爷和儿子关系 
		      0    0     0(d[father]+d[当前])%3 
		      0    1     1(d[father]+d[当前])%3
		      1    1     2
		      1    2     0
		      ......
		    *//*PS:由于食物链是由三种(xx)形成的环,所以只需判断(儿子、父亲和爷爷的关系即可)*/ 
		//由此便可以推出当前爷爷节点和儿子节点的关系
		 
	    return f[x];  
	}
	
	
	int main(){
		cin>>n>>m;
		for (int i=1; i<=n; i++) {f[i]=i; d[i]=0;}
		for (int i=1; i<=m; i++) 
		{scanf("%d%d%d",&p,&x,&y);
		if ((y>n || x>n) || p==2 && x==y) {ans++; continue;}//弱智判断。。。 
		int a=findfat(x),b=findfat(y);//由于每次都会find一次,所以每次并查集都可以更新 
		if (p==1)
		{
			if (a==b){//假设x和y已经在同一个集合里 
				if (d[x]!=d[y]) ans++;
				//若两个对于相同的爷爷(也可以不同,但由于已经在同一个集合里了,这个问题可以忽略,因为本身食物链就是一个环状结构),如果他们的关系不同,则可以说明是假话 
				}
			else 
			{			
			d[a]=(d[y]-d[x]+3)%3;//如果两者不在同一个集合里,则两者此时毫无关系,那么
			//便可以将两者合并 
			//由于d[a],d[b]应该是0,所以d[y]就等于y节点在该环上所处的位置,d[x]也应该是x在该环上所处的位置
			//同样有枚举法可以得到:
			//PS:此时的父节点已经是最顶端的f[x]了 
			//儿子  父亲关于儿子的关系
			//0      (3-0)%3=0
			//1       (3-1)%3=2(说明父亲吃儿子。。。。)
			//2       (3-2)%3=1(说明儿子吃父亲)
			//所以(3-d[x])即在有x的集合中,最顶端父节点和儿子的关系
			//不妨将其设为yw。
			//那么d[a]就应该是y节点的儿子。。。。
			//即.....完毕 
				f[a]=b;
	}
	    }
	    else 
	    {
	    	if (a==b){
	    		if (d[x]!=(d[y]+1)%3) ans++;
	    		//如果在同一个并查集内,却不满足条件,则将其判断为假话 
	    	}
	    	else 
	    	{
			    d[a]=(d[y]-d[x]+4)%3;
			    //同理
				//将x的根节点接在y后面。。。。
				//所以d[a]=d[y]+(3-d[x])——(根节点与x的关系)+1;
				//结束。。。。。 
	    		f[a]=b;//将其合并 
	    	}
	}}
	    cout<<ans<<endl;
	    return 0;
	}

  

posted @ 2017-08-22 11:07  最弱的蒟蒻  阅读(1606)  评论(1编辑  收藏  举报