Time Limit: 1000MS | Memory Limit: 20000KB | 64bit IO Format: %I64d & %I64u |
Description
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
Input
The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output
For each case, output the number of suspects in one line.
Sample Input
100 4 2 1 2 5 10 13 11 12 14 2 0 1 2 99 2 200 2 1 5 5 1 2 3 4 5 1 0 0 0
Sample Output
4 1 1
并查集这个东西,确实非常好用。
并查集学起来很简单,通过想象一下图,也是很好理解的。。通过用father[x]将所有在一个集合的元素并进来成一个大集合。
比较巧妙的地方,在于路径压缩,使得原本时空占用都比较大的一个递归,变得只有O(1)。
用代码讲比较好
#include <iostream> #include <cstdio> #define maxn 30005 using namespace std; int father[maxn]; int size[maxn]; int findset(int x) { if (x!=father[x]) father[x]=findset(father[x]); //这里巧妙的用了路径压缩,让每个点直接保留的是最上面的root节点,之后再查找起来,一步就到了顶点。 return father[x]; } void unionset(int x,int y)//这里进行不同集合的合并 { int r1=findset(x); int r2=findset(y); if (r1==r2) return; if (size[r1]<=size[r2]) //我试了不进行判断,默认以r1为顶点,将其他有关联的集合全部指向它。。但是样例能过,OJ过不了。。一定要加判断才过得了 { father[r1]=r2; //其实我怎么想一下,我觉得不判断也没问题啊。。强制集合内的第一个点为顶点,,还没想明白为什么会有问题。 size[r2]+=size[r1]; //size每次加多少,根据题目要求,这道题要求集合内总人数,无疑是直接加集合内人数。 } else { father[r2]=r1; size[r1]+=size[r2]; } } int main() { int n,m; while (scanf("%d %d",&n,&m)) { if (n==0) break; for (int i=0;i<n;i++) { father[i]=i; size[i]=1; } for (int j=0;j<m;j++) { int k,a; scanf("%d %d",&k,&a); for (int q=0;q<k-1;q++) { int b; scanf("%d",&b); unionset(a,b); } } int ans=size[findset(0)]; printf("%d ",ans); } return 0; }