【题解】永无乡
题目大意:给\(n\)个点及其权值,每次并两个点,会形成一些联通块,求一个点所在联通块的权值第\(k\)小的点的编号。
读起来比较绕口,但实质就那么几个:插入,合并,求第\(K\)小。
我们可以想到用线段树合并。而维护连通性,可以用那个代码量小,短小精悍的数据结构:并查集。
具体来说,当合并\(x,y\)的时候,找到它们的并查集的\(root\),以总体来合并,方便维护信息。合并完之后,将两个点的并查集\(root\)重置一下,记为总的那个根就行。
对于查询,我们可以在树上二分求\(k\)小。无解当且仅当它所在联通块的节点数小于\(k\)。
注意输出是输出点的编号,维护一个映射:\(f:v->Num\)即可。\(Num\)是编号,\(v\)是权值。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=500000;
int rt[MAXN],ch[MAXN<<5][2],rub[MAXN<<5],n,m,tot,cnt;
int v[MAXN],f[MAXN],Q;
int val[MAXN<<5],vb[MAXN];
char c[5];
inline int build(){return (cnt?rub[cnt--]:++tot);}
void del(int x){
ch[x][0]=ch[x][1]=val[x]=0;
rub[++cnt]=x;return;
}
void modify(int &p,int l,int r,int pos,int vv){
if(!p)p=build();
val[p]+=vv;
if(l==r)return;
int mid=l+r>>1;
if(pos<=mid)modify(ch[p][0],l,mid,pos,vv);
else modify(ch[p][1],mid+1,r,pos,vv);
}
int query(int p,int l,int r,int ql,int qr){
if(l>qr||r<ql)return 0;
if(l>=ql&&r<=qr)return val[p];
int mid=l+r>>1;
return query(ch[p][0],l,mid,ql,qr)+query(ch[p][1],mid+1,r,ql,qr);
}
int kth(int p,int l,int r,int k){
if(l==r)return l;
int mid=l+r>>1;
if(val[ch[p][0]]>=k)return kth(ch[p][0],l,mid,k);
else return kth(ch[p][1],mid+1,r,k-val[ch[p][0]]);
}
int merge(int x,int y){
if(!x||!y)return x+y;
val[x]+=val[y];
ch[x][0]=merge(ch[x][0],ch[y][0]);
ch[x][1]=merge(ch[x][1],ch[y][1]);
del(y);return x;
}
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&v[i]),f[i]=i,vb[v[i]]=i;
for(int i=1;i<=n;++i)modify(rt[i],1,n,v[i],1);
for(int i=1;i<=m;++i){
int x,y;
scanf("%d%d",&x,&y);
x=find(x),y=find(y);
if(x==y)continue;
rt[x]=merge(rt[x],rt[y]);f[y]=x;
}
scanf("%d",&Q);
while(Q--){
scanf("%s",c);
int x,y;
scanf("%d%d",&x,&y);
if(c[0]=='B'){
int X=x,Y=y;
X=find(X),Y=find(Y);
if(X==Y)continue;
rt[X]=merge(rt[X],rt[Y]);f[Y]=X;
}
else{
int G=v[x];
x=find(x);
if(y>query(rt[x],1,n,1,n)){
cout<<-1<<endl;
continue;
}
printf("%d\n",vb[kth(rt[x],1,n,y)]);
}
}
return 0;
}