【BZOJ2733】【HNOI2012】永无乡

原题传送门

题意:给你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\n",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;}

 

posted @ 2017-05-04 09:44  Melacau  阅读(295)  评论(0编辑  收藏  举报