poj1182~食物链~种类并查集

思路:首先需要这个小技巧,不然很难写

kind[a]=0表示a与父节点属于同一类。kind[a]=1表示a吃父节点。kind[a]=2表示父节点吃a。 (后二种情况下的赋值可以改变,但对后续有点小影响)1.有一种关系b是a父节点,c是b父节点,  a与c的关系可以表示为 (kind[a]+kind[b])%3    (延续性,适用于多个节点的延续,如3个节点根据二次计算即可完成)

2.b是a的父节点,表示为kind[a].  若父子节点相互反转,即a是b的父节点,kind[b]=(3-kind[a])%3  (反转性)根据延续性和反转性可计算任何两个节点之间的关系,以下是几个例子:

现在讨论并查集中用到关系的3中情况:

(i):find中的更新,x的父节点是y,y的父节点为根节点,将关系更新为x的父节点为根节点,表示为:kind[x]=(kind[x]+kind[y])%3;

(ii)并查集中的合并。  假设x的父节点是xx  y的父节点是yy  x和y的关系为d。若将xx的父节点更新为yy,则 kind[xx]=kind[x]的反转(即xx的父节点为x)+d(x的父节点为y)+kind[y](y的父节点为yy),简化为kind[xx]=(3-kind[x]+d+kind[y])%3.

(iii)并查集中判断x,y是否冲突x和y的父节点都为同一个根节点,知道kind[x],kind[y],d(表示x和y的关系)  3-kind[x](x的反转,根节点->x)+d(x->y)+kind[y](y->根节点)  根据延续性这个式子表示为  根节点->根节点的关系  即 (3-kind[x]+d+kind[y])%3==0

#include<iostream>
#include<string>
#include<fstream>
using namespace std;
#define M 50005

int pre[M],v[M];
int find(int x)
{
	/*int y=x,t=0,temp;    //非递归情况,不知道为什么用这个就会WA
	while(y!=pre[y]) 
	{
		t+=v[y];
		y=pre[y];
	}
	while(x!=y)
	{
		//t-=v[x];
		v[x]=t%3;
		t-=v[x];
		temp=pre[x];
		pre[x]=y;
		x=temp;
	}
	return y;*/
	int y;              //递归find
	if(x!=pre[x])
	{
		y=pre[x];
		pre[x]=find(y);
		v[x]=(v[x]+v[y])%3;
	}
	return pre[x];
}

void uni(int x,int y,int d)
{
	int px,py;
	px=find(x);
	py=find(y);
	pre[px]=py;
	v[px]=(3-v[x]+d-1+v[y])%3;
}


int main()
{
	int n,k,i,j,d,x,y,px,py;
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++)
	{
		pre[i]=i;
		v[i]=0;
	}
	int ans=0;
	while(k--)
	{
		scanf("%d%d%d",&d,&x,&y);
		if((d==2&&x==y)||x>n||y>n )
			ans++;
		else 
		{
			px=find(x);
			py=find(y);
			if(px!=py) 
				uni(x,y,d);
			else if( (v[x]-v[y]+3)%3!=d-1 )
				ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值