poj1182--食物链(带权并查集经典题)

题意:m次查询,每次查询都只有两种格式:
1 x y表示x与y是同类;
2 x y 表示x吃y;
问其中有多少句假话。

1.当x,y中某个标号大于n时,直接判假;
2.判断矛盾:这里有一个很好用的方法:就是用数学中的向量去看待每一条边权,并规定方向,这样,通过向量的加减运算就可以很直观的得出之间的权值关系了(手动画图很清晰)。由于这个链是一个循环链也就是说数值之间的变化必然会循环在某些数字之中;
设关系(y->x):0为x与y同类;1为x吃y,2为x被y吃(两个元素之间仅存在这些可能的关系)当然数字的选择还是有一些意义的,这样的话:每次输入的d-1就是表示x与y的关系了。(这里是三种关系的循环,故每次运算要%3保持在这个关系圈内)
对于关系转移
(1)并查集路径压缩中:当父节点变化时通过递归更新每个父节点的权值,最后通过子节点与父节点之间的向量(变量:子,父,父父:三角形法则)加法(要取模保证关系循环!)更新子节点的权值。
(2)合并中:这时出现了四个变量(x,fx,y,fy,四边形中进行向量加法运算),注意点:合并时一定要每次都是合并到同一棵树上。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 50050
//关系中权值为0表示两者为同类;
//a->b权值为1表示a吃b;
//a->b权值为2表示a被b吃,即b吃a; 
//用向量加减法进行关系之间的运算处理,手动画图;
int f[maxn],dis[maxn];//定义父亲数组,当前节点与根节点之间的权值数组;
int n,k;
int ans;
inline void ini()
{
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		dis[i]=0;
	}
	ans=0;
}

int find(int x)
{
	if(x==f[x])
	{
		return x;
	}
	else
	{
		int father=f[x];
		f[x]=find(f[x]);
		dis[x]=(dis[x]+dis[father])%3;
	}
	return f[x];
}
 
void connect(int a,int b,int d)
{
	int fa=find(a);
	int fb=find(b);
	if(fa==fb && ((dis[a]+d-1+3)%3)!=dis[b])
	{
		ans++;
	}
	else
	if(fa!=fb)
	{
		f[fa]=fb;
		dis[fa]=(dis[b]-d+1-dis[a]+3)%3;
	}
}
 
int main()
{
	int i,j;
	int d,a,b;
	scanf("%d%d",&n,&k);
	ini();
	for(i=1;i<=k;i++)
	{
		scanf("%d%d%d",&d,&a,&b);
		if(a>n||b>n)
		{
			ans++;
		}
		else
		{
			connect(a,b,d);
		}
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值