题意:
解法:
集合合并容易想到用并查集。
因为还需要查询学生x所在集合有多少个c(x)=y的学生,
所以对每个学生开一棵权值线段树,这样可以做到高效查询,
权值线段树动态开点,集合合并的时候直接合并两颗权值线段树即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
int lc[maxm*40],rc[maxm*40],cnt[maxm*40];
int rt[maxm],tot;
int pre[maxm];
int n,m;
int ffind(int x){
return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
void add(int x,int l,int r,int &k){
k=++tot;
if(l==r){
cnt[k]++;
return ;
}
int mid=(l+r)/2;
if(x<=mid)add(x,l,mid,lc[k]);
else add(x,mid+1,r,rc[k]);
}
int merged(int x,int y){
if(!x||!y)return x+y;
lc[x]=merged(lc[x],lc[y]);
rc[x]=merged(rc[x],rc[y]);
cnt[x]+=cnt[y];
return x;
}
int ask(int x,int l,int r,int k){
if(!k)return 0;
if(l==r)return cnt[k];
int mid=(l+r)/2;
if(x<=mid)return ask(x,l,mid,lc[k]);
else return ask(x,mid+1,r,rc[k]);
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
pre[i]=i;
int x;cin>>x;
add(x,1,n,rt[i]);
}
while(m--){
int op,x,y;cin>>op>>x>>y;
if(op==1){
x=ffind(x),y=ffind(y);
if(x==y)continue;
pre[x]=y;
rt[y]=merged(rt[x],rt[y]);
}else{
x=ffind(x);
int ans=ask(y,1,n,rt[x]);
cout<<ans<<endl;
}
}
return 0;
}