POJ.1611 The Suspects (并查集)
POJ.1611 The Suspects (并查集)
题意分析
对于每组测试数据:
这个学校有n个人,m小组,依次给出m个小组的信息,每行一个小组。
每个小组的信息包括,k,表示有k个人,k个数字,分别是这k个人的ID。
现在ID为0的人感染了病毒,他及他的小组成员全部会感染病毒,每个小组成员都会感染自己所在小组的成员(每个成员可以加入多个小组)。问现在总共有多少个人感染了病毒?
比较裸的并查集。
我们可以把每个人看作是一个集合,对于一个小组的成员,就是不停地在做并集操作。并集的时候,同时要合并他们下面的节点个数。最后只要找到0的根是谁,输出其下的节点个数即可。
代码总览
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <sstream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#define INF 0x3f3f3f3f
#define nmax 30005
#define MEM(x) memset(x,0,sizeof(x))
using namespace std;
int father[nmax];
int rnk[nmax];//秩
int num[nmax];//爸爸下节点的个数,集合中元素个数
void makeset(int x)
{
father[x] = x;//自己是自己的爸爸
rnk[x] = 0;//秩为0
num[x] = 1;//爸爸下面元素个数为1
}
int findset(int x)
{
int r = x,temp;
while(father[r] != r) r = father[r];// 一直向上找
//压缩路径
while(r != x){
temp = father[x];//暂存爸爸
father[x] = r;//直接把爸爸改成根
x = temp;//向上
}
return r;
}
void unionset(int x, int y)
{
x = findset(x);
y = findset(y);
if(x == y) return;// 根相同 不用合并
if(rnk[x] > rnk[y]){
father[y] = x; // 小的并到大的上
num[x]+=num[y];
}else{
father[x] = y;
if(rnk[x] == rnk[y])
rnk[x]++;
num[y]+=num[x];
}
}
int main()
{
//freopen("in.txt","r",stdin);
int n,m;
while(scanf("%d %d",&n,&m)){
if(n == 0 && m == 0) break;
if(m == 0){
printf("1\n");
continue;
}
for(int i = 0; i<n;++i) makeset(i);//每个人自己是一个set
for(int j = 0; j<m;++j){
int num,first,temp; scanf("%d",&num);
for(int k = 0; k<num;k++){
if(!k) scanf("%d",&first);
else{
scanf("%d",&temp);
unionset(first,temp);
}
}
}
int rt = findset(0);
printf("%d\n",num[rt]);
}
return 0;
}