Zjnu Stadium(带权并查集)

题目链接:Problem - 3047 (hdu.edu.cn) Zjnu Stadium

题目大意:给出关于n个人座位的m个要求,每个要求由三个数A、B、C。表示B在A后面C个单位距离。需要你从这m个要求中,找出其中不能满足的个数。

分析:这道题考查的是带权并查集,如果没看出来我建议先看一下后面的一个板子题(这种需要判断的题一看就想到了并查集)。如果A与B不在一个集合中,合并两个集合即可,若在一个集合,判断A与B的距离是不是恰好为C即可,如果不是,累计答案。这道题的核心是实现带权并查集的合并操作。我们设A集合的根节点为f1,B集合的根节点为f2,合并的时候, f [ f 2 ] = f 1 ; d [ f 2 ] = d [ A ] + C − d [ B ] ; f[f2]=f1;d[f2]=d[A]+C-d[B]; f[f2]=f1;d[f2]=d[A]+Cd[B];​​​。

我们不难知道合并后应该满足, d [ f 1 ] < = d [ A ] < d [ f 2 ] < = d [ B ] d[f1]<=d[A]<d[f2]<=d[B] d[f1]<=d[A]<d[f2]<=d[B]​​,而且前两个不需要更新的。我们只需要更新 d [ f 2 ] d[f2] d[f2]就行, d [ B ] d[B] d[B]是可以通过 d [ f 2 ] d[f2] d[f2]更新的。为什么是这个值,建议自己画一个数轴去理解一下。​

#include<bits/stdc++.h>
using namespace std;
const int N=50000+5;
int f[N],d[N];
int n,m;
int find(int x){//刘汝佳蓝书的板子
	if(f[x]!=x){
		int root=find(f[x]);
		d[x]+=d[f[x]];
		return f[x]=root;
	}
	return x;
}
int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		int ans=0;
		for(int i=0;i<=n;i++) f[i]=i,d[i]=0;
		for(int i=1,x,y,z;i<=m;i++){
			scanf("%d%d%d",&x,&y,&z);
			int f1=find(x),f2=find(y);
			if(f1!=f2){
				f[f2]=f1;
				d[f2]=d[x]+z-d[y];
			}
			else{
				if(d[y]-d[x]!=z) ans++;
			}
		} 
		printf("%d\n",ans);
	}
	return 0;
}

以下是带权并查集的板子题

Corporative Network - UVALive 3027 - Virtual Judge (vjudge.net) Corporative Network

有两个操作,第一个操作给你一数X,求X到根节点的距离。第二个操作是给你两个数X和Y

,表示Y到X的距离为abs(x-y)%1000,X是Y的父节点,且题目保证X在该操作前没有父节点。

如果不理解带权并查集的find()操作,建议多跑几个例子。

#include<bits/stdc++.h>
using namespace std;
const int N=20005;
const int mod=1000;
int T,n,f[N],d[N];
int find(int x){
	if(x!=f[x]){
		int root=find(f[x]);
		d[x]+=d[f[x]];
		return f[x]=root;
	}
	return f[x];
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=0;i<=n;i++){
			f[i]=i,d[i]=0;
		}
		char ch;	
		while(scanf(" %c",&ch)&&ch!='O'){
			if(ch=='I'){
				int x,y;
				scanf("%d%d",&x,&y);
				f[x]=y;
				d[x]=abs(x-y)%mod;
			}
			else{
				int x;
				scanf("%d",&x);
				find(x);
				printf("%d\n",d[x]);
			}
		}
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值