首先连通块我们用并查集维护,而查询k大我们用权值线段树维护
建边就是合并两个线段树
查询直接在权值线段树查询就好了
#include<cstdio> #include<algorithm> using namespace std; int n,m,q,a[100005],f[100005],v[100005],root[100005],cnt; struct SegTree{ int nl,nr,sum; }tree[2000005]; int Find(int x){return x==f[x]?x:f[x]=Find(f[x]);} void Insert(int &rt,int l,int r,int pos){ if(!rt)rt=++cnt; if(l==r){tree[rt].sum=1;return;} int mid=l+r>>1; if(pos<=mid)Insert(tree[rt].nl,l,mid,pos); else Insert(tree[rt].nr,mid+1,r,pos); tree[rt].sum=tree[tree[rt].nl].sum+tree[tree[rt].nr].sum; } int Query(int rt,int l,int r,int k){ if(l==r)return l; int mid=l+r>>1; if(tree[tree[rt].nl].sum>=k)return Query(tree[rt].nl,l,mid,k); return Query(tree[rt].nr,mid+1,r,k-tree[tree[rt].nl].sum); } int Merge(int l,int r){ if(!l)return r; if(!r)return l; tree[l].nl=Merge(tree[l].nl,tree[r].nl); tree[l].nr=Merge(tree[l].nr,tree[r].nr); tree[l].sum=tree[tree[l].nl].sum+tree[tree[l].nr].sum; return l; } int main(){ freopen("2733.in","r",stdin); freopen("2733.out","w",stdout); scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){scanf("%d",&a[i]);f[i]=i;} for(int i=1;i<=m;i++){ int tp,tq; scanf("%d %d",&tp,&tq); tp=Find(tp);tq=Find(tq);f[tp]=tq; } for(int i=1;i<=n;i++){Insert(root[Find(i)],1,n,a[i]);v[a[i]]=i;} scanf("%d",&q); while(q--){ char s[5]; int x,y; scanf("%s %d %d",s,&x,&y); if(s[0]=='B'){x=Find(x);y=Find(y);if(x!=y){f[x]=y;root[y]=Merge(root[x],root[y]);}} if(s[0]=='Q'){x=Find(x);if(tree[root[x]].sum>=y)printf("%d\n",v[Query(root[x],1,n,y)]);else puts("-1");} } return 0; }