P2024 [NOI2001] 食物链 带权并查集 循环关系

题目: 

P2024 [NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 

本文学习自: 

题解 P2024 【食物链】 - RE: 从零开始的异世界信竞生活 - 洛谷博客 (luogu.com.cn) 

————

关系并查集其实就是在普通并查集的基础上额外开个数组re,用来表示每个点与其根节点的关系。

这个其实很好理解。设0为同类,1为该点吃根节点,2为根节点吃该点。 

难处理的就是合并,以及压缩并查集时,re关系数组如何处理。

只需理解这个图:

 

为什么有这个等式,其实这个等式左右两边表示的都是A与F2的关系 。(A到F2的两条路径和)

再结合关系是循环的(A吃B,B吃C,C吃D,那么A和D就是同类,构成循环了)。

然后就是注意,减法可能减负的,可以加个模数再取模。

代码: 

两个find,第一个是压缩时,循环处理re关系数组

第二个注释掉的是递归法,回溯的时候处理re关系数组

int fa[50005];
//带权并查集
int rela[50005];

void init(int _size)
{
	for (int i = 0; i <= _size; i++)
		fa[i] = i;
}
int find(int aim)
{
	int cur = aim;
	int sum = 0;
	while (fa[aim] != aim)
	{
		sum += rela[aim];//存
		aim = fa[aim];
	}
	while (fa[cur] != cur)
	{
		int tmp = cur;
		cur = fa[cur];
		fa[tmp] = aim;

		sum -= rela[tmp];
		rela[tmp] = (sum+rela[tmp]) % 3;
	}
	return aim;
}
//int find(int aim)//从根往下更新
//{
//	int father = fa[aim];
//	if (fa[aim] == aim)
//		return aim;
//	fa[aim] = find(father);
//	rela[aim] = (rela[aim] + rela[father]) % 3;
//	return fa[aim];
//}
void join(int a, int b,int op)
{
	int oa = a, ob = b;
	a = find(a);
	b = find(b);
	fa[a] = b;
	rela[a] = (op + rela[ob] - rela[oa]+3)%3;

	//只处理祖先就好了,其余在压缩的时候处理
	//并查集就是合并根
}


void solve()
{
	int n, k;
	cin >> n >> k;
	init(n);
	int op, a, b;
	int ans = 0;
	for (int i = 1; i <= k; i++)
	{
		cin >> op >> a >> b;
		if (a > n || b > n)
		{
			ans++;
			continue;
		}
		if (op == 1)
		{
			if (find(a) == find(b))
			{
				if(rela[a] != rela[b])
					ans++;			
			}
			else
			{
				join(a, b,0);
			}
		}
		else
		{
			if (a == b)
			{
				ans++;
				continue;
			}
			//如果在一个集合中,要判断关系是否正确
			if (find(a) == find(b))
			{
				if (rela[a] != (1 + rela[b]) % 3)
				{
					ans++;
				}
			}
			else
				join(a, b,1);
		}
	}
	cout << ans << endl;
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值