4018
Problem R
Time Limit : 15000/8000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 4 Accepted Submission(s) : 2
5 6 M 0 1 M 1 2 M 1 3 S 1 M 1 2 S 3 3 1 M 1 2 0 0
Case #1: 3 Case #2: 2
题意:有N 封邮,编号 0 -> N-1, 然后有两种操作, M : 合并操作, 2 种邮件合并为一种;S : 分离操作, 将一封邮件独立出去单独占一个集合,
最后题目要求统计集合的个数. 从这里可以很容易的看出这是一个并查集的题目, 不过按朴素方法来做的一般都会TLE.
思路:因为并查集是树形结构,所以无法简单的把一个节点从一棵树中删去并维护原来的信息。那这里用到的思想就是还是保持原来的树的结构不变,只是把被删掉的那个点设为虚点,并新建一个点,把原来的点映射到这个新点上,代表以后的操作都是对这个新点进行操作。这样空间开销虽然大,但还是可以解决问题的。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
#include<queue>
#include<set>
using namespace std;
typedef long long LL;
typedef pair<int,int> P;
const int maxn = 2000000 + 5;
const int INF = 1000000000;
int cnt;
int fa[maxn],id[maxn];
int Find(int x){return fa[x]==x?fa[x]:fa[x]=Find(fa[x]);}
set<int> S;
int main(){
int n,m;
int kase = 0;
while(scanf("%d%d",&n,&m)){
if(n == 0 && m == 0) break;
kase++;
cnt = n;
for(int i = 0;i < maxn;i++) fa[i] = i;
for(int i = 0;i < n;i++) id[i] = i;
while(m--){
char s[5];
int a,b;
scanf("%s",s);
if(s[0] == 'M'){
scanf("%d%d",&a,&b);
int X = Find(id[a]);
int Y = Find(id[b]);
if(X != Y) fa[X] = Y;
}
else{
scanf("%d",&a);
id[a] = cnt++;
}
}
S.clear();
for(int i = 0;i < n;i++){
S.insert(Find(id[i]));
}
printf("Case #%d: %d\n",kase,S.size());
}
return 0;
}