并查集

例题:

C. Rumor
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Vova promised himself that he would never play computer games... But recently Firestorm — a well-known game developing company — published their newest game, World of Farcraft, and it became really popular. Of course, Vova started playing it.

Now he tries to solve a quest. The task is to come to a settlement named Overcity and spread a rumor in it.

Vova knows that there are n characters in Overcity. Some characters are friends to each other, and they share information they got. Also Vova knows that he can bribe each character so he or she starts spreading the rumor; i-th character wants ci gold in exchange for spreading the rumor. When a character hears the rumor, he tells it to all his friends, and they start spreading the rumor to their friends (for free), and so on.

The quest is finished when all n characters know the rumor. What is the minimum amount of gold Vova needs to spend in order to finish the quest?

Take a look at the notes if you think you haven't understood the problem completely.

Input

The first line contains two integer numbers n and m (1 ≤ n ≤ 105, 0 ≤ m ≤ 105) — the number of characters in Overcity and the number of pairs of friends.

The second line contains n integer numbers ci (0 ≤ ci ≤ 109) — the amount of gold i-th character asks to start spreading the rumor.

Then m lines follow, each containing a pair of numbers (xi, yi) which represent that characters xi and yi are friends (1 ≤ xi, yi ≤ nxi ≠ yi). It is guaranteed that each pair is listed at most once.

Output

Print one number — the minimum amount of gold Vova has to spend in order to finish the quest.

Examples
input
Copy
5 2
2 5 3 4 8
1 4
4 5
output
10
input
Copy
10 0
1 2 3 4 5 6 7 8 9 10
output
55
input
Copy
10 5
1 6 2 7 3 8 4 9 5 10
1 2
3 4
5 6
7 8
9 10
output
15
Note

In the first example the best decision is to bribe the first character (he will spread the rumor to fourth character, and the fourth one will spread it to fifth). Also Vova has to bribe the second and the third characters, so they know the rumor.

In the second example Vova has to bribe everyone.

In the third example the optimal decision is to bribe the first, the third, the fifth, the seventh and the ninth characters.

题意:给n个人传话,给每个人传话都要一定费用,一部分人是朋友,给其中一个人传话所有人都会知道这件事。问最小花费。可以用bfs,也可以用并查集(更快)。

#include <cstdio>
#include <iostream>
#define MAX_N 100005
typedef long long ll;
using namespace std;
int co[MAX_N],pre[MAX_N];
int n,m,a,b;
ll ans=0;

int find(int x)
{
	if (pre[x]==x)
		return x;
	else 
		return pre[x]=find(pre[x]);
} //查询函数

void inline combine(int p,int q)
{
	int x=find(p),y=find(q);
	if (x==y) return;
	if (co[x]>co[y]) 
		pre[x]=y;
	else pre[y]=x;
} //合并函数

int main()
{
	cin >> n >> m;
	for (int i=1;i<=n;i++)
	{
		pre[i]=i;
		scanf("%d",&co[i]);
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		combine(a,b);
	}
	for (int i=1;i<=n;i++)
		if (pre[i]==i) ans+=co[i];
	cout << ans;
	return 0;
}

并查集简单的说就是实现两个操作,一个是并,另一个是查。

核心代码:

并:

void inline combine(int p,int q)
{
	int x=find(p),y=find(q); //找各自的根节点
	if (x==y) return; //根节点相同就直接返回
        pre[x]=y; //将x的头接到y的尾上
}

查:

int find(int x)
{
	if (pre[x]==x)
		return x;  //如果x的前驱是自己那说明x就是根节点
	else 
		return pre[x]=find(pre[x]);  //否则就向前找
} //递归实现

L2-4 部落(25 分)

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

输入格式:

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

K P[1] P[2]  P[K]

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

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

输出格式:

首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出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

题意:看题目就可以用并查集来做。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int pre[10005],show[10005];
int n,k,t,m,a,b,sum=0,sum1=0;

int find(int x)
{
	if (pre[x]==x) 
		return x;
	else return pre[x]=find(pre[x]);
}

int combine(int p,int q)
{
	int x=find(p),y=find(q);
	if (x==y) return 0;
	else 
	{
		pre[x]=y;
		return 1;
	}
}

int main()
{
	memset(show,0,sizeof(show));
	cin >> n;
	for (int i=1;i<=1000;i++)
		pre[i]=i;
	for (int i=0;i<n;i++)
	{
		cin >> m >> a;
		if (show[a]==0) {show[a]=1; sum++;}
		for (int j=1;j<m;j++)
		{
			scanf("%d",&b);
			if (show[b]==0) {show[b]=1; sum++;}
			sum1+=combine(a,b);
		}
	}
	cout << sum << " " << sum-sum1 << endl;
	cin >> t;
	for (int i=0;i<t;i++)
	{
		cin >> a >> b;
		if (find(a)==find(b))
			cout << "Y" << endl;
		else cout << "N" << endl;
	}
	return 0;
}
Ubiquitous Religions
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions:38869 Accepted: 18530

Description

There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in. 

You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.

Input

The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.

Output

For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.

Sample Input

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

Sample Output

Case 1: 1
Case 2: 7

Hint

Huge input, scanf is recommended.

Source


题意:简单并查集练手。

#include <cstdio>
#include <iostream>
typedef long long ll;
using namespace std;
int pre[50005],n,m,cnt=0,ans,a,b;

int find(int x)
{
	if (pre[x]==x) return x;
	else return pre[x]=find(pre[x]);
}

void combine(int n,int m)
{
	int x=find(n),y=find(m);
	if (x==y) return;
	pre[y]=x;
}

int main()
{
	while(cin >> n >> m && n+m)
	{
		ans=0;
		for (int i=1;i<=n;i++)
			pre[i]=i;
		for (int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b);
			combine(a,b);
		}
		for (int i=1;i<=n;i++)
			if (pre[i]==i) ans++;
		printf("Case %d: %d\n",++cnt,ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值