PTA天梯 L2-024 部落(并查集)

在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

输入格式:
输入在第一行给出一个正整数 N ( ≤ 1 0 4 ) N(≤10^4 ) N104,是已知小圈子的个数。随后 N N N行,每行按下列格式给出一个小圈子里的人:

K , P [ 1 ] , P [ 2 ] ⋯ P [ K ] K ,P[1], P[2] ⋯ P[K] KP[1]P[2]P[K]

其中K是小圈子里的人数, P [ i ] ( i = 1 , ⋯ , K ) P[i](i=1,⋯,K) P[i]i=1,,K是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过 1 0 4 10^4 104

之后一行给出一个非负整数 Q ( ≤ 1 0 4 ) Q(≤10^4 ) Q104,是查询次数。随后 Q Q Q行,每行给出一对被查询的人的编号。

输出格式:
首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出 Y Y Y,否则输出 N N N

输入样例:

4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7

输出样例:

10 2
Y
N

对于本题中要求的求出人的个数,可以使用set来存人,这样就可以自动屏蔽掉重复的人,而题目之后要求求出不联系的部落的个数并且还要询问任意两个人是否是一个部落,这里可以把部落看成一个个集合,这个问题可以使用并查集来解决。

#include<iostream>
#include<set>
using namespace std;
const int N = 1e4 + 10;

int p[N];			//并查集存祖宗节点的数组
int find(int x) {	//找到祖宗节点的find函数
	if (p[x] != x)p[x] = find(p[x]);
	return p[x];
}

int main() {
	int n; cin >> n;
	set<int>s;	//用set来存人

	for (int i = 1; i <= N; i++)p[i] = i;//一定要初始化并查集,i的祖宗节点初始化为i

	for (int i = 1; i <= n; i++) {		 //读入
		int k; cin >> k;

		int b; cin >> b;			//先读一个数
		s.insert(b);

		for (int j = 2; j <= k; j++) {
			int a; cin >> a;

			p[find(b)] = find(a);	//把b的祖宗节点指向a的祖宗节点,合并两个集合
			s.insert(a);
		}
	}

	int num = 0;
	for (int i = 1; i <= s.size(); i++) {		//遍历所有人数
		if (p[i] == i)num++;					//如果第i个人的祖宗节点是他自己,那就说明有一个新的集合
	}

	cout << s.size() << " " << num << endl;			//s.size()为总人数,num为没有联系的集合个数

	int q; cin >> q;
	while (q--) {
		int a, b; cin >> a >> b;

		if (find(a) == find(b))cout << "Y" << endl;	//如果在一个集合中就输出Y
		else cout << "N" << endl;
	}

	return 0;
}
  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值