题目描述
素质拓展结束,39找到了小伙伴,他灰常的开森,But,39累了一天了,他打算去洗袜子。39有很多的袜子,所以即使不洗袜子,也能穿很久。但是一旦把所有袜子穿过了,就要一次性洗好多好多的袜子,39就是这么干的。洗好后需要晾干,39把这些袜子胡乱的放到了几个晾衣架上就挂了起来。
袜子有很多双,每双袜子都不一样,而39并不记得全部的袜子的样子,他只记得几种。如果39的袜子和其他袜子在同一个晾衣架上,那么可以肯定,这些袜子都是39的。于是问题来了,请你告诉39,他能识别出多少双袜子以及有多少种袜子是自己的。
输入格式
第一行,三个整数n,m,x,表示有n双袜子,编号从1到n;m是39已知的属于自己袜子种数;x为x个晾衣架。
第二行,m个整数,表示39所知道的属于自己的袜子的编号。
接下来的x行,开头一个整数a,后面跟着a个整数,表示这个晾衣架上挂着的袜子的编号。
输出格式
输出两个整数,用空格分开,表示39能识别出多少双袜子以及有多少种袜子是自己的。
样例输入
5 2 2
1 2
4 1 2 3 4
1 5
样例输出
4 4
提示
1<= n,m,x <=100,000;保证挂在架子的袜子的双数<=n,并且每种袜子不会超过4双。
题目没有“只”和“双”的陷阱
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
int e[2200100];
int b[2201000];
int w[2201000];
int n,m,s,r,num,k,ans;
const double pi=acos(-1.0);
typedef long long ll;
int find(int x1)//并查集一下啦
{
if(e[x1]==x1)
{
return x1;
}
return e[x1]=find(e[x1]);
}
void check(int x,int y)//并查集一下啦
{
x=find(x);
y=find(y);
if(x!=y)
{
e[x] = y;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;//输入袜子总的个数//事先知道的自己袜子的个数//晾衣架的个数
int ans2 = 0;
for (int i = 1 ; i <= n ; i++)//最开始每个袜子的主人都是自己//也就是还未处理
{
e[i] = i;
}
int first ; //将第一个单独罗列出来 相当于可以将其看为一个起点 后面添加的都是分支
cin >> first;//也就是说后面的添加的都将直接查询first也就ok了
for ( int i = 2 ; i <= m; i++)//输入已知自己的袜子并将其与first合并
{
int x;
cin >> x;
check(first,x);
}
for ( int i = 1; i <= k; i++)//输入每个衣架上的袜子//k为衣架的个数
{
int temp;
cin >> temp;//每个衣架的袜子个数
int first_s;
cin >> first_s;//同样将第一个独立出来
w[first_s]++;//w数组用来记录该种袜子的个数//first++因为first也是袜子
for( int j = 2; j <= temp; j++)//
{
int a;
cin >> a;//输入袜子的名称
w[a]++;//数量++
check(a, first_s);//冰茶寄一下啦
}
}
for (int i = 1; i<= n ; i++)//从1到n开始查找 (种类查找
{
if(find(i) == find(first))//之所以不用fa[i]==fa[first]是因为大部分i与first现在并未建立真正的联系
{//如果是同一个祖先(起点
ans +=w[i];//数量++
ans2++;//种类++
}
}
cout<< ans << " " << ans2<<endl;
return 0;
}