1389:亲戚(并查集举一反三的优化)

该博客主要介绍了如何利用并查集数据结构解决大规模亲戚关系查询的问题。在给定的亲戚关系图中,通过建立并查集实现快速判断两个人是否为亲戚,并在O(log n)的时间复杂度内计算家族人数。文章提供了40分和100分两种解决方案,分别适用于简单的判断和统计家族人数,优化了查询效率。

1389:亲戚


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 8949     通过数: 2802

【题目描述】

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的某个人所在家族的人数。

规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

【输入】

第一行:三个整数n,(n≤100,000,m≤200,000),分别表示有n个人,m个信息。

以下m行:信息包含两种形式:

M a b:表示a和b具有亲戚关系。

Q a:要求输出a所在家族的人数。

【输出】

要求输出a所在家族的人数。

【输入样例】

5 10
M 3 2
Q 4
M 1 2
Q 4
M 3 2
Q 1
M 3 1
Q 5
M 4 2
Q 4

【输出样例】

1
1
3
1
4

一,40分的思路

很简单,直接判断输入的字母是否是M,如果是则join(x,y),否则先用一个y存储x的b最大boss,再遍历所有人,看看有几个人的最大boss和y相等,输出即可。

代码:

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int n,m,fa[1000001],x,y,s;
char orz;
int fin(int k)
{
	if(k == fa[k]) return k;
	return fa[k] = fin(fa[k]);
}
void join(int p,int q)
{
	int fp = fin(p),fq = fin(q);
	if(fp != fq) fa[fq] = fp;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++) fa[i] = i;
	while(m--)
	{
	  scanf("%s",&orz);
	  if(orz == 'M')
	  {
	    scanf("%d%d",&x,&y);
		join(x,y);
	  }
	  else
	  {
	    scanf("%d",&x);
	    y = fin(x);
		s = 0;
		for(int i = 1;i <= n;i++)
		  if(fin(i) == y)
		    s++;
		printf("%d\n",s);
	  }
	}
	return 0;
}

二,100分的思路

跟之前的差不多,但是要加一个siz数组来储存有第i个人的集团人数。那么join函数也要改改:

void join(int c1, int c2) 
{ 
	int f1 = fin(c1), f2 = fin(c2); 
	if(f1 != f2)
	{
		if(siz[f1] < siz[f2]) swap(f1, f2);
		fa[f2] = f1; 
		siz[f1] += siz[f2]; 
	} 
}

整体代码:

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int n,m,fa[1000001],siz[1000001],x,y,s;
char orz;
int fin(int k)
{
	if(k == fa[k]) return k;
	return fa[k] = fin(fa[k]);
}
void join(int c1, int c2) 
{ 
	int f1 = fin(c1), f2 = fin(c2); 
	if(f1 != f2)
	{
		if(siz[f1] < siz[f2]) swap(f1, f2);
		fa[f2] = f1; 
		siz[f1] += siz[f2]; 
	} 
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++) fa[i] = i,siz[i] = 1;
	while(m--)
	{
		scanf("%s",&orz);
		if(orz == 'M')
		{ 
			scanf("%d%d",&x,&y);
			join(x,y);
		}
		else
		{
			scanf("%d",&x);
			printf("%d\n",siz[fin(x)]);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值