题目描述:
题目链接:ZOJ 3641 Information Sharing
题目大意:
几个小朋友每个都会几个知识点。现在有三个操作:
-
arrive
Name
m
a1a2..am :表示加入一个孩子名叫 Name ,并且这个孩子会m个知识点分别是a1a2 ..am。 - share Name1 Name2 :表示名叫Name1和Name2的孩子共享知识点。且共享具有传递性。即,share a b,然后再 share a c,那么c也可以学会b所掌握的知识点。
- check Name : 询问名叫Name的孩子在和其他孩子共享知识点之后会多少知识点。
解题思路:
显然这是明显的并查集的应用。我们需要不断的将
share
之后的孩子们放入同一个集合。最后在cheak的时候只要输出当前孩子所在集合的大小即可。这里的技巧是通过
map
将孩子的名字由
string
映射为数字
int
。以此来方便在
set
和并查集中的操作。
关于并查集的介绍:并查集详解
复杂度分析:
时间复杂度 :
O(n)
空间复杂度 :
O(n)
AC代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <map>
#include <set>
using namespace std;
const int maxn = 100010;
int kase = 0,np;
string op,name,name2;
map<string,int>child;
set<int>info[maxn];
int f[maxn];
int FIND(int x){
return f[x]==x?x:f[x] = FIND(f[x]);
}
void arrive(){
cin >> name;
if(child[name] == 0) child[name] = ++np;
int t,m;
cin >> m;
while(m--){
cin >> t;
info[child[name]].insert(t);
}
}
void share(){
cin >> name >> name2;
int a = child[name], b = child[name2];
int x = FIND(a), y = FIND(b);
if(x != y){
f[x] = y;
set<int>::iterator it;
for(it = info[x].begin(); it != info[x].end(); ++it)
info[y].insert(*it);
info[x].clear();
}
}
void check(){
cin >> name;
int x = FIND(child[name]);
cout << info[x].size() << endl;
}
int main(){
int n;
while(cin >> n){
for(int i = 0; i <= n; ++i){
f[i] = i;
info[i].clear();
}
child.clear();
np = 0;
while(n--){
cin >> op;
if(op == "arrive")arrive();
else if(op == "share")share();
else if(op == "check" )check();
}
}
return 0;
}