带权值的并查集

题目链接:【POJ 1182】食物链

父节点跟子节点之间一共有三种关系,父节点吃子节点,子节点吃父节点,父节点跟子节点是同类,所以用三进制表示

fa[B]=A,rank[B]=0表示与父节点是同类,rank[B]=1表示被父节点吃,rank[B]=2表示吃父节点

一共分两大类进行操作:1、x跟y的父节点相同  2、x跟y的父节点不同

下面是我做题时画的图,方便理解,父节点跟子节点之间的关系有9种,箭头指出去的表示被吃,箭头指进来的表示吃父节点

1、x跟y的父节点相同

只有满足(rank[y]-rank[x]+3)%3==id-1时,那输入的id x y这句话才是真的


2、x跟y的父节点不相同

xi是x的父节点,yi是y的父节点

黑线表示父节点跟子节点之间的关系

1) id=1,x跟y是同一类


2) id=2,x吃y


通过画图可以发现,当父节点不同时都是真话(除了id=2&&x==y),需要我们做的就是找到相应的xi与yi间的关系,更新节点权值

fa[yi] = xi && rank[yi]=(rank[x]-rank[y]+id+3)%3

上面的纯粹是理解用的,理论知识可以点击这里

当然,要是公式总结不出来,直接通过上图用if else来判断也是可以的,就是麻烦了点

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <string>
using namespace std;
const int N=50001;
int fa[N], rank[N];
void init(int n)
{
	for(int i=1; i<=n; i++)
	{
		fa[i]=i, rank[i]=0;
	}
}
int find(int x)
{
	if(x!=fa[x])
	{
		int fx=find(fa[x]);
		rank[x]=(rank[x]+rank[fa[x]])%3;
		fa[x]=fx;
	}
	return fa[x];
}
bool Union(int id, int x, int y)
{
	int xi=find(x), yi=find(y);
	if(xi==yi) 
	{
		if((rank[y]-rank[x]+3)%3==id) return false;
		return true;
	}
	fa[yi] = xi;
	rank[yi]=(rank[x]-rank[y]+id+3)%3;
	return false;
}
int main()
{
	int n, k, id, x, y, ans=0;
	scanf("%d%d", &n, &k);
	init(n);
	while(k--)
	{
		scanf("%d%d%d", &id, &x, &y);
		if(x>n || y>n || (x==y&&id==2)) ans++;
		else if(Union(id-1, x, y)) ans++;
	}
	printf("%d\n", ans);
	return 0;
} 

题目链接:【POJ 1733】 Parity game


题目链接:【POJ 2492】 A Bug's Life


题目链接:【POJ 1703】 Find them, Catch them


题目链接:【POJ 1988】 Cube Stacking


题目链接:【POJ 2912】 Rochambeau


题目链接:【HDU 3038】 How Many Answers Are Wrong

有n个数,m次询问,给出a到b区间的总和s,问这m次给出的总和中有几次是和前面已近给出的是矛盾的

sum[i]记录的是i到根节点的区间和,fa[i]记录的根节点永远比i小

输入注意要加while(~scanf("")),否则会wa!

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int N=1e5+10;
int fa[2*N], sum[2*N];
int find(int x)
{
	if(x!=fa[x])
	{
		int fx=find(fa[x]);
		sum[x]+=sum[fa[x]];
		fa[x]=fx;
	}
	return fa[x];
}
bool Union(int a, int b, int s)
{
	int ai=find(a), bi=find(b);
	if(ai==bi)
	{
		if(sum[a]+s != sum[b]) return true;
	}
	else //这里画画图就能得出结论 
	{
		if(ai>bi)
		{
			fa[ai]=bi;
			sum[ai]=sum[b]-sum[a]-s;
		}
		else
		{
			fa[bi]=ai;
			sum[bi]=sum[a]+s-sum[b];		
		}
	}
	return false;
}
int main()
{
	int n, m, a, b, s;
	while(~scanf("%d%d", &n, &m))
	{
		int ans=0;
		for(int i=0; i<=n; i++) fa[i]=i, sum[i]=0;
		while(m--)
		{
			scanf("%d%d%d", &a, &b, &s);
			if(Union(a-1, b, s)) ans++;
		}
		printf("%d\n", ans);
	}
	return 0;
}

题目链接:【HDU 3047】 Zjnu Stadium


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值