一起学猫叫病毒( Miaomiaomiao Together), 是一种原因不明的流行性病毒。从去年起,一起学猫成为新一代校园毒品,听到一起学猫叫这首歌的人很有可能就会感染这种病毒。由于它传染性很强( 只要你周围有人唱一起学猫叫,你很有可能也会开始唱,并开始劝你的同学一起唱)它开始被认为是全球威胁。为了减少传播给别人的机会, 最好的策略是隔离可能的患者。
学校里有许多学生团体。同一组的学生经常彼此相通,一个学生可以同时加入几个小组。为了防止一起学猫叫病毒病毒的传播,cjluxk算法与程序设计协会收集了所有学生团体的成员名单。他们的标准操作程序如下:
一旦一组中有一个可能的患者, 组内的所有成员就都是可能的患者。
为了遏制这种病毒的传播,我们需要找到所有的患者。现在知道编号为0的如花妹妹(感染源)已经得了一起学猫叫病毒,请你设计程序 发现所有可能的患者。
Input
输入文件包含多组数据。
对于每组测试数据:
第一行为两个整数n和m, 其中n是学生的数量, m是团体的数量。0 < n <= 30000,0 <= m <= 500。
每个学生编号是一个0到n-1之间的整数,一开始只有0号的如花妹妹被视为患者。
紧随其后的是团体的成员列表,每组一行。
每一行有一个整数k,代表成员数量。之后,有k个整数代表这个群体的学生。一行中的所有整数由至少一个空格隔开。
n = m = 0表示输入结束,不需要处理。
Output
对于每组测试数据, 输出一行可能的患者。
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
思路
求同一连通分量中的结点个数,在并查集中再开一个size数组存储,注意同一连通分量中的结点个数存储在以它的根结点为下标的size元素。
代码
package uf;
import java.util.Scanner;
public class Poj1611 {
private static class UF{
private int[] parent;
private byte[] rank;
private int[] size;
UF(int N){
parent = new int[N];
rank = new byte[N];
// size[k]储存以k为根结点的连通分量中的结点个数,初始化为1
size = new int[N];
for(int i = 0; i < N; i++){
parent[i] = i;
size[i] = 1;
}
}
private int find(int p){
while (p != parent[p]){
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
public void union(int p, int q){
int rootP = find(p), rootQ = find(q);
if(rootP == rootQ) return;
if(rank[rootP] < rank[rootQ]){
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}else if(rank[rootP] > rank[rootQ]){
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
}else {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
rank[rootP]++;
}
}
}
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while (true){
int m = in.nextInt(), n = in.nextInt();
if(m == 0 && n == 0) break;
UF uf = new UF(m);
while (n-- > 0){
int t = in.nextInt();
int a, b;
a = in.nextInt();
while (--t > 0){
b = in.nextInt();
uf.union(a, b);
a = b;
}
}
// 这里注意0不一定是它的连通分量的根结点。
// 所以0的同一连通分量中的结点的个数存储在以它的根结点为索引的size数组中的元素。
System.out.println(uf.size[uf.find(0)]);
}
}
}