【Atcoder】ABC183 F - Confluence 并查集+启发式合并
题意
现在有 n 个学生,第 i 个学生所在班级为 \(c_i\)。
现在他们要去上学,某个学生在去上学的路上可能会遇到其他的学生并加入到该集合。
现在有两种操作:
- x y,表示第 x 个学生所在集合和第 y 个学生所在集合相遇
- x y 表示查询和第 x 个学生在同一个集合中 y 班级的学生。
思路
第一个很明显是并查集,但第二个查询却不好解决。
首先不可能维护每个集合中所有班级的学生数量,数组都开不了这么大。
稍微优化一下,只维护出现在该集合中的班级的学生数量。
那合并的时候我们怎么办呢?
启发式合并
暴力将集合较小的合并到较大的中去。
使用一个二维map来维护每个集合中出现了那些班级以及这些班级的学生数量。
代码
#include <algorithm>
#include <iostream>
#include <map>
#include <stdio.h>
#include <string.h>
#define pb push_back
typedef long long ll;
using namespace std;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int fa[N];
map<int, map<int, int>> mp;
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
int c[N];
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
mp[i][c[i]] = 1;
fa[i] = i;
}
while (q--) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
int xx = find(x), yy = find(y);
if (xx != yy) {
if (mp[xx].size() > mp[yy].size()) {
swap(xx, yy);
}
fa[xx] = yy;
for (map<int, int>::iterator it = mp[xx].begin(); it != mp[xx].end(); ++it) {
mp[yy][it->first] += it->second;
}
}
} else {
int xx = find(x);
map<int, int>::iterator pos = mp[xx].find(y);
if (pos != mp[xx].end())
printf("%d\n", mp[xx][y]);
else
printf("0\n");
}
}
return 0;
}