题意:给你N个带权点,一开始相互独立(每个点视为单独一个集合),有2种操作:1)合并2个集合;2)查询包含某元素集合内的权值第k小点编号。
解题思路:显然合并就是并查集,而查询则是平衡树实现。
考虑对每个集合开一棵平衡树,这样的话直接合并2棵平衡树的效率最坏是( n log n )的,显然会TLE。
考虑使用启发式合并,这样就可以将合并的集合树的深度严格限制在( log n )内,于是每次合并的效率就约为 ( log^{2} n )的。这样就不会TLE了。
时间效率:操作1:( log^{2} n ) ; 操作2: ( log n ).
注意一下如果已经在一个集合内就不要合并了。
总复杂度:( O( (m+q) log^{2} n) ) / ( O(n) ).
AC代码:(1312ms/4688KB on BZOJ)
#include <stdio.h> #define r register #define MN 100005 #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),S==TT)?EOF:*S++) char BB[1<<15],*S=BB,*TT=BB; inline int read(){ r int x=0,f=1; r char ch=getchar(); while (ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar(); while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; }inline int rad(){ static int x=23333; return x^=x<<13,x^=x>>17,x^=x<<5; } struct treap{ treap *ls,*rs; int sz,val,ord,pri; void combine(){ sz=1; if (ls!=NULL) sz+=ls->sz; if (rs!=NULL) sz+=rs->sz; }treap(int val,int ord):ord(ord),val(val){ls=rs=NULL,sz=1,pri=rad();} }*root[MN]; inline void lturn(treap* &x){r treap *y=x->rs; x->rs=y->ls; y->ls=x; y->sz=x->sz; x->combine(); x=y;} inline void rturn(treap* &x){r treap *y=x->ls; x->ls=y->rs; y->rs=x; y->sz=x->sz; x->combine(); x=y;} inline void Insert(treap* &x,int val,int ord){ if (x==NULL) {x=new treap(val,ord);return;}x->sz++; if (val<x->val){Insert(x->ls,val,ord);if (x->ls->pri<x->pri) rturn(x);} else{Insert(x->rs,val,ord);if (x->rs->pri<x->pri) lturn(x);} } inline void merge(treap* &o,treap* &x){ if (o==NULL) return; merge(o->ls,x);merge(o->rs,x); Insert(x,o->val,o->ord); delete o;o=NULL;return; } inline int query(treap *x,int k){ if (x==NULL||k<1||k>x->sz) return -1; if (x->ls==NULL){ if (k==1) return x->ord; return query(x->rs,k-1); } if (k<=x->ls->sz) return query(x->ls,k); if (k==x->ls->sz+1) return x->ord; return query(x->rs,k-x->ls->sz-1); } int fa[MN],n,q; inline int getfa(int x){return fa[x]?fa[x]=getfa(fa[x]):x;} void init(){ n=read(),q=read(); for (int i=1; i<=n; ++i) Insert(root[i],read(),i); while(q--){ r int x=getfa(read()),y=getfa(read()); if (x==y) continue; if (root[x]->sz<root[y]->sz) merge(root[x],root[y]),fa[x]=y; else merge(root[y],root[x]),fa[y]=x; } } void solve(){ q=read();while (q--){ r char op=getchar();while(op!='Q'&&op!='B') op=getchar(); r int x=read(),y=read(); if (op=='Q') printf("%d ",query(root[getfa(x)],y)); else{ x=getfa(x),y=getfa(y); if (x==y) continue; if (root[x]->sz<root[y]->sz) merge(root[x],root[y]),fa[x]=y; else merge(root[y],root[x]),fa[y]=x; } } } int main(){init(); solve(); return 0;}