[USACO11OPEN]Learning Languages
题目描述
农夫约翰的 N ( 2 < = N < = 10 , 000 ) N(2<=N<=10,000) N(2<=N<=10,000)只奶牛标号为 1.. N 1..N 1..N,同样的有 M ( 1 < = M < = 30 , 000 ) M(1<=M<=30,000) M(1<=M<=30,000)种牛语标号为 1.. M 1..M 1..M,第i只奶牛会说 K i ( 1 < = K i < = M ) K_i(1<=K_i<=M) Ki(1<=Ki<=M)种牛语,分别为 L i 1 , L i 2 , … , L i K i ( 1 < = L i j < = M ) L_i1,L_i2,…,L_{iK_i}(1<=L_ij<=M) Li1,Li2,…,LiKi(1<=Lij<=M),农夫的奶牛不是特别聪明,所以 K i K_i Ki的累加和不大于 100 , 000 100,000 100,000。
两只奶牛只有当他们至少有一门语言一样的时候才可以沟通。否则这两只奶牛就需要别人来帮他们翻译才能交流。换句话说,A和B要进行沟通,他们可以通过 T 1 , T 2 , … , T k T_1,T_2,…,T_k T1,T2,…,Tk来传递,比如A和 T 1 , T 1 T_1,T_1 T1,T1和 T 2 , … , T k T_2,…,T_k T2,…,Tk和B进行交流。
农夫希望他的奶牛可以多多沟通,所以他买了很多课本去教她的奶牛语言。当然农夫非常的吝啬,他希望买最少的书就可以让所有的奶牛可以交流。你的任务就是帮他算出最少需要买几本书。
输入格式
* Line 1: Two space-separated integers: N and M
第二行到第N+1行,每一行第一个数 K i K_i Ki代表会的语言个数,而后K个是语言编号
输出格式
算出最少需要买几本书。
样例 #1
样例输入 #1
3 3
2 3 2
1 2
1 1
样例输出 #1
1
题解/启示
看到此类有间接字眼的问题,第一时间想到并查集,这题的本质就是小的并查集要并到大的并查集,要注意的就是注意统计问题。
AC代码
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
typedef int ll;
const ll maxn = 3e4 + 10;
ll fa[maxn], wt[maxn], k2[maxn], cnt[maxn]; // wt代表高度,cnt统计语言出现次数,也标志各语言是否出现(是否非0),
//并不是m个语言都出现,k2是tmp数组
ll n, m, res;
ll find(ll x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void unite(ll x1, ll x2)
{
ll fa1 = find(x1);
ll fa2 = find(x2);
if (fa1 == fa2)
return;
if (wt[fa1] > wt[fa2])
{
fa[fa2] = fa1;
}
else
{
fa[fa1] = fa2;
}
if (wt[fa1] == wt[fa2])
wt[fa2]++;
}
int main()
{
//一开始文件名有非法符号_,故不能通过,有中文“桌面”也不行,估计是编译器或编辑器的编码问题
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
ll k;
cin >> k;
ll tmp;
cin >> tmp;
if (!find(tmp))//若未初始化,即代表该语言第一次出现,进行初始化
{
fa[tmp] = tmp;
wt[tmp] = 1;
cnt[tmp]++;
}
if (k == 1)
continue;
for (int j = 2; j <= k; j++)
{
cin >> k2[j];
if (!find(k2[j]))//若未初始化,即代表该语言第一次出现,进行初始化
{
wt[k2[j]] = 1;
fa[k2[j]] = k2[j];
cnt[k2[j]]++;
}
unite(k2[j], tmp);
}
}
ll max1 = -1;
ll maxidx = -1;
for (int i = 1; i <= m; i++)
{
if (find(i) == 0) //错因1:不是每个语言都被学了,一开始没注意
{
continue;
}
ll tmp = ++cnt[find(i)];//统计语言出现次数,后续补要从次数小的并查集往大的并查集里靠,很有必要
if (max1 < tmp)
{
max1 = tmp;
maxidx = fa[i];
}
}
for (int i = 1; i <= m; i++)
{
if (cnt[i] >= 1 && find(i) != maxidx) //错因2:一开始使用fa[i] != maxidx,未能及时更新fa[i]
{
unite(i, maxidx);
res++;
}
}
cout << res << endl;
return 0;
}