• 【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
    ",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;}
  • 相关阅读:
    每天进步一点点------Allegro 布线完成后如何修改线宽
    每天进步一点点------Allegro 布线时显示延迟以及相对延迟信息
    每天进步一点点------Allegro 修线
    每天进步一点点------Allegro 蛇形走线
    每天进步一点点------Allegro 动态显示走线长度
    每天进步一点点------Allegro 群组布线
    每天进步一点点------Allegro 手工布线时控制面板各选项说明
    每天进步一点点------Allegro 铺铜、内电层分割
    每天进步一点点------Allegro 铺铜详解
    每天进步一点点------Allegro使用脚本记录文件设置工作环境的颜色
  • 原文地址:https://www.cnblogs.com/Melacau/p/BZOJ2733.html
Copyright © 2020-2023  润新知