sdnu 1078 食物链(并查集)

原题链接:http://210.44.14.31/problem/show/1078


relation存的是  该结点相对于父结点的关系。

0代表同类  1代表父吃子2代表子吃父


理解三个公式:

①压缩关系公式:

(relation[儿子]+relation[父亲])%3== 儿子和爷爷的关系

②合并根节点关系公式:

((d-1)+3-ani[儿子2].relation+ani[儿子1].relation) % 3==儿子1的父亲与儿子2的父亲的关系

③在根节点相同、d==2的情况下,判定当前条件的关系是否与已知的关系冲突:

(3 - relation[儿子1] + relation[儿子2]) % 3 ==1


穷举法均可证明三个公式的正确性。

(证明本写完一遍了,可发表后空格全乱了)


特别注意:

①路径压缩时应保证父结点的正确性,可递归调用。

②cin超时



代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 50000 + 10;
int n, k;
int relation[N], pre[N];		//记录与父节点的关系和父结点

int find(int animal)	//在之前我写过的非递归调用的路径压缩会导致利用了不正确的父结点而导致错误
{
	if (animal == pre[animal])
		return animal;
	int temp = pre[animal];
	pre[animal] = find(temp);
	relation[animal] = (relation[animal] + relation[temp]) % 3;//公式①
	return pre[animal];
}

void combine(int X,int Y, int x, int y, int d)//合并根节点
{
	pre[Y] = X;
	relation[Y] = ((d - 1) + 3 - relation[y] + relation[x]) % 3;//公式②
}

void init(int n)//初始化
{
	for (int i = 1; i <= n; i++)
	{
		relation[i] = 0;
		pre[i] = i;
	}
}
int main()
{
	int ans = 0;
	scanf("%d%d", &n, &k);
	init(n);
	while (k--)
	{
		int d, x, y;
		scanf("%d%d%d", &d, &x, &y);
		if ((x > n || y > n) || (d == 2 && x == y))	//①超范围   ②自己吃自己
			ans++;
		else
		{
			int X = find(x);
			int Y = find(y);
			if (X != Y)	//不同根结点时,合并
			{
				combine(X, Y, x, y, d);
			}
			else
			{
				if (d == 1 && relation[x] != relation[y])//若 x与y同类,则两者与同一根结点的关系应该  一致
					ans++;
				else if (d == 2 && (3 - relation[x] + relation[y]) % 3 != 1)	//公式③
					ans++;
			}
		}
		
	}
	cout << ans << endl;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值