【题解】永无乡

(Question)

题目大意:给(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
",vb[kth(rt[x],1,n,y)]);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/12562065.html