这道题是一道经典的平衡树+启发式合并吧。那么考虑用可持久化线段树来写。
对每一个节点保存一棵线段树表示所在块的编号的集合(因此可以一个块值保存一棵树),然后合并的时候就地柜合并左子节点和右子节点,然后更新节点的值即可。时空复杂度O(NlogN)
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
#define M 2000005
using namespace std;
int n,m,trtot,a[N],id[N],fa[N],rt[N],sum[M],ls[M],rs[M];
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x;
}
void ins(int &k,int l,int r,int v){
if (!k) k=++trtot; if (l==r){ sum[k]=1; return; }
int mid=(l+r)>>1;
if (v<=mid) ins(ls[k],l,mid,v); else ins(rs[k],mid+1,r,v);
sum[k]=sum[ls[k]]+sum[rs[k]];
}
int merge(int x,int y){
if (!x || !y) return x+y;
ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]);
sum[x]=sum[ls[x]]+sum[rs[x]]; return x;
}
int qry(int k,int rst){
int l=1,r=n; if (sum[k]<rst) return -1;
while (l<r){
int tmp=sum[ls[k]],mid=(l+r)>>1;
if (tmp>=rst){ k=ls[k]; r=mid; }
else{ k=rs[k]; l=mid+1; rst-=tmp; }
}
return id[l];
}
int getfa(int x){ return (x==fa[x])?x:fa[x]=getfa(fa[x]); }
int main(){
n=read(); m=read(); int i,x,y;
for (i=1; i<=n; i++){
a[i]=read(); fa[i]=i; id[a[i]]=i;
}
while (m--){
x=getfa(read()),y=getfa(read());
if (x!=y) fa[x]=y;
}
for (i=1; i<=n; i++) ins(rt[getfa(i)],1,n,a[i]);
m=read(); char ch;
while (m--){
ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
x=read(); y=read();
if (ch=='B'){
x=getfa(x); y=getfa(y);
if (x!=y){ fa[x]=y; rt[y]=merge(rt[x],rt[y]); }
} else printf("%d\n",qry(rt[getfa(x)],y));
}
return 0;
}
by lych
2016.3.10