传送门
Sol
- 一眼看上去是水题,没想到跪了一上午。。。
- 看到第k小想到平衡树,维护连通性用并查集。因此主要思路就是维护一个平衡树森林,合并操作就把两个平衡树合并
- 难点在于,合并暴力肯定TLE,因此需要启发式合并
void dfs(int p,int &f){
if(lc) dfs(lc,f);
if(rc) dfs(rc,f);
int x,y;
split(f,val[p],x,y);
renode(p);
f=merge(merge(x,p),y);
}
- 代码是fhq treap。虽然就是把一颗平衡树拆掉再一个一个加到另一颗树里。但是启发式合并的巧妙在于,它是把节点数小的树拆掉合并到大的树上,于是均摊复杂度就成了优雅的
O(nlogn)
- 合并的时候还有一些细节:节点信息的清空和并查集的更新
Code
// by spli
using namespace std;
const int N=100010*2;
int n,m,q,sz;
int c[N],pos[N];
int lnk[N];
int siz[N];
int col[N],rt;
int ch[N][2];
int pri[N];
int val[N];
int _find(int x){
if(x!=lnk[x]) lnk[x]=_find(lnk[x]);
return lnk[x];
}
int newnode(int id){
sz++;
siz[sz]=1;
val[sz]=c[id];
pos[sz]=id;
pri[sz]=rand();
return sz;
}
void pushup(int p){
siz[p]=1;
if(lc) siz[p]+=siz[lc];
if(rc) siz[p]+=siz[rc];
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(pri[x]<pri[y]){
ch[x][1]=merge(ch[x][1],y);
pushup(x);
return x;
}
else{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);
return y;
}
}
void split(int p,int v,int &x,int &y){
if(!p){
x=0,y=0;
return;
}
if(val[p]<=v) x=p,split(rc,v,rc,y);
else y=p,split(lc,v,x,lc);
pushup(p);
}
int getmin(int p){
while(1){
if(lc) p=lc;
else return val[p];
}
}
void renode(int p){
lc=rc=0;
}
void dfs(int p,int &f){
if(lc) dfs(lc,f);
if(rc) dfs(rc,f);
int x,y;
split(f,val[p],x,y);
renode(p);
f=merge(merge(x,p),y);
}
void DFS(int p){
if(lc) DFS(lc);
cout<<pos[p]<<" ";
if(rc) DFS(rc);
}
void un(int x,int y){
int r[2];
r[0]=_find(x);
r[1]=_find(y);
if(r[0]==r[1]) return;
if(siz[r[0]]<siz[r[1]]) swap(r[0],r[1]);
dfs(r[1],r[0]);
lnk[_find(x)]=lnk[_find(y)]=r[0];
lnk[r[0]]=r[0];
}
int get_th(int p,int k){
while(1){
if(k<=siz[lc]) p=lc;
else if(k==siz[lc]+1) return p;
else k-=siz[lc],k--,p=rc;
}
}
void query(int a,int k){
int rt=_find(a);
if(siz[rt]<k) puts("-1");
else printf("%d\n",pos[get_th(rt,k)]);
}
int main(){
srand(19260817);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&c[i]);
//ins(i,c[i]);
col[i]=newnode(i);
}
for(int i=1;i<=n;++i) lnk[i]=i;
int a,b;
while(m--) scanf("%d%d",&a,&b),un(a,b);
scanf("%d",&q);
int x,y,c,d;
char op[10];
while(q--){
scanf("%s",op);
scanf("%d%d",&x,&y);
if(op[0]=='B') un(x,y);
if(op[0]=='Q') query(x,y);
}
return 0;
}