POJ 1182 食物链

还没有很好的理解,先把AC代码段贴在这里,再慢慢消化。总体思路是用fat[i]存i个动物的父节点,ran[i]存i和父亲的关系:0表示同类,1表示吃父亲,2表示被父亲吃。首先初始化所有动物节点的父节点为本身,(fat[i] = i)。和父亲的关系为同类(ran[i] = 0)。然后接受输入,首先查询输入的根节点是否相同(即判断是否属于同一棵树)(查询的同时进行了路径压缩),属于同一棵树就判断这句话是真是假,即(ran[x] - ran[y] + 3) % 3 == typ - 1 (1)为真则这句话为真,否则为假。

若查询的根节点不同(即不属于同一颗树),那么这句话和前面的就不矛盾,合并x和y所在的树。即    fat[x1] = y1 

 ran[x1] = (-ran[x] + typ - 1 + ran[y] + 3) % 3;  (2)

这里这两个式子的获得有一定的难度

 

/*n个动物 k句话 有一种循环a吃b 吃c c吃a 开始不知道n种动物关系是什么
两种询问:d=1 x y为同类  d=2 x吃y 判断假话条数(关键之违背之前的关系)
并查集可以很好解决的满足区间传递关系的区间合并问题,注意一般是多棵树*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Max = 50010;
int fat[Max], ran[Max];
void Init(int n)//初始化重要
{
	for (int i = 0; i <= n; i++)
	{
		fat[i] = i;//初始化都是指向(看做)自己
		ran[i] = 0;//0同类 1吃父节点 2被父节点吃
	}
	return;
}
int Find(int x)//找寻父节点+路径压缩
{
	if (x == fat[x])
		return fat[x];
	int y = Find(fat[x]);
	ran[x] = (ran[x] + ran[fat[x]]) % 3;//递归后从祖先节点向后到每个孩子来计算,看和根节点是吃还是被吃的关系
	fat[x] = y;//路径压缩
	return y;
}
int Union(int typ, int x, int y)//区间并与查询
{
	int x1 = Find(x);
	int y1 = Find(y);
	if (x1 == y1)//属于同一棵树(共父节点),判断出关系
	{
		if ((ran[x] - ran[y] + 3) % 3 == typ - 1)//ran[x]和ran[y]都是跟根节点的关系,ran[x]=ran[y]则同类,ran[x]=ran[y]+1
			return 0;
		else return 1;
	}
	fat[x1] = y1;//连接两父节点
	ran[x1] = (-ran[x] + typ - 1 + ran[y] + 3) % 3;//使用类似向量方法来计算权值,虽然题目只有两个,但是会出现被吃这种情况,所以要变成3种情况,注意一定要处理负数的情况
	return 0;
}
int main()
{
	int n, k, ans;
	int typ, smt1, smt2;
	scanf("%d %d", &n, &k);
	Init(n);
	ans = 0;
	for (int i = 0; i<k; i++)
	{
		scanf("%d %d %d", &typ, &smt1, &smt2);
		if (smt1 == smt2&&typ == 2)
			ans++;
		else if (smt1>n || smt2>n)
			ans++;
		else
			ans += Union(typ, smt1, smt2);
	}
	printf("%d\n", ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值