FZU 2155(并查集的删除)

Problem 2155 盟国

Accept: 219    Submit: 778
Time Limit: 5000 mSec    Memory Limit : 32768 KB

 Problem Description

世界上存在着N个国家,简单起见,编号从0~N-1,假如a国和b国是盟国,b国和c国是盟国,那么a国和c国也是盟国。另外每个国家都有权宣布退盟(注意,退盟后还可以再结盟)。

定义下面两个操作:

“M X Y” :X国和Y国结盟

“S X” :X国宣布退盟

 Input

多组case。

每组case输入一个N和M (1 ≤ N ≤ 100000 , 1 ≤ M ≤ 1000000),N是国家数,M是操作数。

接下来输入M行操作

当N=0,M=0时,结束输入

 Output

对每组case输出最终有多少个联盟,格式见样例。

 Sample Input

5 6M 0 1M 1 2M 1 3S 1M 1 2S 33 1M 1 20 0

 Sample Output

Case #1: 3Case #2: 2



题解:在训练赛的时候没有写出来,当时是判断并查集的删除,但是很遗憾的是没有做过类似的题目,这里把题目补了一下,HDU上貌似有个一模一样的题目,不过那一题的时限更宽,这里如果使用set,会卡logN,数据更强。

这一题使用虚点的形式去分离点,如果某个点它从集合中删除,那么就把它放在一个新的根下,你可能会想直接把这个点的根置为自己不就可以了吗?那如果这个点本身就是根的情况下,这个点根本就没有从集合中分离出来。也有说直接使用动态树就好了,表示还不会呀




#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include<set>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define N (int)(2e6+20)
int fa[N], real[N],pos[N];
void init(int n)
{
	for (int i = 0; i<n; i++)
	{
		fa[i] = i;
		real[i] = i;
		pos[i] = 0;
	}
}

int find(int x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unio(int x, int y)
{
	x = find(x);
	y = find(y);
	if (x != y)fa[x] = y;
}
int ans = 0;
int main()
{
#ifdef CDZSC
	freopen("i.txt", "r", stdin);
#endif
	char s[10];

	int n, m, x, y, cas = 0, cnt;
	while (~scanf("%d%d", &n, &m))
	{
		if (n == 0 && m == 0)break;
		cnt = n;
		int ans = 0;
		init(n + m + 1);
		while (m--)
		{
			scanf("%s", s);
			if (s[0] == 'M')
			{
				scanf("%d%d", &x, &y);
				x = real[x];
				y = real[y];
				unio(x, y);
			}
			else
			{
				scanf("%d", &x);
				real[x] = ++cnt;
				fa[cnt] =cnt;
			}
		}
		for (int i = 0; i<n; i++)
		{
			int x = find(real[i]);
			if (!pos[x])
			{
				ans++;
				pos[x] = 1;
			}
		}
		printf("Case #%d: %d\n", ++cas,ans);
	}
	return 0;
}







你可能感兴趣的:(FZU 2155(并查集的删除))