题意
给定n个人,第i个人属于c[i]班,然后给出q个操作:
1 a b表示将a,b两个人所在的集合合并
2 x y表示询问跟x同一集合的人中有几个是y班的
思路
一般合并集合很容易想到并查集,用map存储第i个人所在集合里每个班的人数,所以就可以开个数组sz[i]表示i所在集合的大小,然后合并的规则是启发式合并,就是将sz小的集合合到sz大的集合里面(据说这样复杂度更优一些),等下合并的时候就把小集合里面每个班的人数情况对应的加到大集合里面的相应班级里,然后更新大集合的sz,下面看代码。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
map<int,int> m[N];
int s[N],sz[N],a[N];
void initial(int n){
for (int i=1;i<=n;i++){
s[i]=i;
sz[i]=1;
}
}
int find(int x){
return x==s[x]?x:s[x]=find(s[x]);
}
int main(){
int n,op,q;
scanf("%d %d",&n,&q);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for (int i=1;i<=n;i++){
m[i][a[i]]++;
}
initial(n);
int x,y;
while (q--){
scanf("%d %d %d",&op,&x,&y);
if (op==1){
x=find(x);
y=find(y);
if (x==y) continue;
if (sz[x]>sz[y]) swap(x,y);
for (auto &v:m[x]){
m[y][v.first]+=m[x][v.first];
}
sz[y]+=sz[x];
s[x]=y;
}
else{
x=find(x);
printf("%d\n",m[x][y]);
}
}
return 0;
}