题目大意: 非典来袭 n 个人 m 个团队, n个人的编号为0 --- n-1 其中已知 0 被怀疑携带非典病毒,如果一个队伍中有一个人是
被怀疑携带有此病毒,那么这一个团队中的所有人都被i怀疑携带有非典病毒。
输入格式:
第一行 n m 代表 n个人 m个团队
接下来的m行 每一行代表一个团队 每一行的第一个数为团队人数 后面接上 此团队的人员编号
直到输入中 n m全为0 方才结束。
输出:所有可能携带次病毒的人员的数量。
解析:很明显 用并查集 另外 考虑一下极端情况
如果输入的数据为:
30000 1
3 0 29999 15000
0 0
那么 真是哦存在的只有三个人 0 15000 29999 如果i循环从0一个一个枚举 则不太好
好一点的方法就是用优先队列 将这三个数存储起来 用的时候 一个一个弹出 即可。
代码C:
# include <stdio.h>
# define N 30001
void insert(int Tree[],int X);//向堆中插入值 S[WEI]
int Delmin(int Tree[]); //删除堆头-并返回堆头
int find(int x);
void Link(int x,int y);
int tree[N+1]; //堆
int p[N],Q[N];
int main()
{
int n,m,a,b,c,i,j,k;
// freopen("AAA.txt","r",stdin);
while(scanf("%d%d",&n,&m)&&(n||m))
{
while(m--)
{
scanf("%d%d",&a,&c);
c++;
Link(c,c);
while(--a>0){
scanf("%d",&b);
if(find(++b)!=c)Link(b,c);
}
}
n=0;
while(tree[0])Q[n++]=Delmin(tree);
for(i=k=0,c=find(1); i<n; i++)
if(p[Q[i]])
{
if(find(Q[i])==c)tree[++k]=Q[i];
else p[Q[i]]=0;
}
if(k)printf("%d\n",k);
else printf("1\n");
while(k)p[tree[k--]]=0;
}
return 0;
}
void insert(int Tree[],int X)//向最小堆中插入X
{
int par,i=++Tree[0]; //插入X 后 Tree[0]+1
while(i>1) //直到i不是根节点
{
par=(i>>1); //父节点为par
if(Tree[par]<=X) break; //将<=改为>=即改为最大堆了
Tree[i]=Tree[par]; //否则调整堆 即位置上移
i=par;
}
Tree[i]=X; //插入新节点
}
int Delmin(int Tree[])
{
int i=1,L=2,root=Tree[1],X=Tree[Tree[0]--];
while(L<=Tree[0])
{
L+=L<Tree[0]&&Tree[L+1]<Tree[L];
if(Tree[L]>=X) break;
Tree[i]=Tree[L];
i=L;
L=i<<1;
}
Tree[i]=X;
return root;
}
int find(int x)
{
int y=x;
if(p[x])while(x!=p[x]) x=p[x];
else insert(tree,x);
return p[y]=x;
}
void Link(int x,int y)
{
p[find(x)]=find(y);
}