【bzoj2733】永无乡(无旋treap启发式合并 + 并查集)

传送门

题目分析

  起初每个岛都是一个平衡树, 并查集的祖先都是自己。合并两岛时,pri较小的祖先会被作为合并后的祖先, 而两颗平衡树采用启发式合并。查询k值就是基本操作。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

const int N = 1e5 + 5;
int n, m;
#define SZ(x) (x?x->sze:0)
struct node{
    node *lc, *rc;
    int pri, sze, val, num;
    inline node* upt(){
        sze = SZ(lc) + SZ(rc) + 1;
        return this;
    }
}pool[N], *tail = pool, *nod[N];
int fa[N];

inline int Rand(){
    static int RAND_VAL = 1388593021;
    return RAND_VAL += RAND_VAL << 2 | 1;
}

inline node* newNode(int v, int k){
    node *x = ++tail;
    x->sze = 1, x->val = v, x->pri = Rand(), x->num = k;
    return x;
}

inline node* Merge2(node *u, node *v){
    if(!u) return v;
    if(!v) return u;
    if(u->pri < v->pri){
        u->rc = Merge2(u->rc, v);
        return u->upt();
    }
    else{
        v->lc = Merge2(u, v->lc);
        return v->upt();
    }
}

inline int getAnc(int a){
    return fa[a] == a ? a : (fa[a] = getAnc(fa[a]));
}

inline void Split_v(node *u, int v, node *&x, node *&y){
    if(!u){
        x = y = NULL;
        return;
    }
    if(u->val <= v){
        Split_v(u->rc, v, x, y);
        u->rc = NULL, u->upt();
        x = Merge2(u, x);
    }
    else{
        Split_v(u->lc, v, x, y);
        u->lc = NULL, u->upt();
        y = Merge2(y, u);
    }
}

inline void Split_k(node *u, int k, node *&x, node *&y){
    if(!u){
        x = y = NULL;
        return;
    }
    if(SZ(u->lc) < k){
        Split_k(u->rc, k - SZ(u->lc) - 1, x, y);
        u->rc = NULL, u->upt();
        x = Merge2(u, x);
    }
    else{
        Split_k(u->lc, k, x, y);
        u->lc = NULL, u->upt();
        y = Merge2(y, u);
    }
}

inline node* Merge(node *u, node *v){
    if(!u) return v;
    if(!v) return u;
    node *L, *R;
    if(u->pri > v->pri) swap(u, v);
    Split_v(v, u->val, L, R);
    u->lc = Merge(u->lc, L);
    u->rc = Merge(u->rc, R);
    return u->upt();
}

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-';  ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline void wr(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

int main(){
    n = read(), m = read();
    for(int i = 1; i <= n; i++) fa[i] = i;
    for(int i = 1; i <= n; i++) nod[i] = newNode(read(), i);
    for(int i = 1; i <= m; i++){
        int a = read(), b = read();
        int fu = getAnc(a), fv = getAnc(b);
        if(fu != fv){
            if(nod[fu]->pri < nod[fv]->pri) fa[fv] = fu, nod[fu] = Merge(nod[fu], nod[fv]);
            else fa[fu] = fv, nod[fv] = Merge(nod[fu], nod[fv]);
        }
    }
//    for(int i = 1; i <= n; i++) cout<<i<<":"<<nod[i]->sze<<endl;
    int Q = read();
    for(int i = 1; i <= Q; i++){
        char opt[5]; scanf("%s", opt);
        if(opt[0] == 'Q'){
            node *L, *R, *p, *q;
            int x = read(), k = read(), fx = getAnc(x);
//            cout<<x<<" "<<k<<" "<<fx<<" "<<endl;
            Split_k(nod[fx], k - 1, L, R);
            Split_k(R, 1, p, q);
            if(p) wr(p->num);
            else wr(-1);
            putchar('\n');
            nod[fx] = Merge2(L, Merge2(p, q));
        }
        else{
            int a = read(), b = read();
            int fu = getAnc(a), fv = getAnc(b);
            if(fu != fv){
                if(nod[fu]->pri < nod[fv]->pri) fa[fv] = fu, nod[fu] = Merge(nod[fu], nod[fv]);
                else fa[fu] = fv, nod[fv] = Merge(nod[fu], nod[fv]);
            }
        }
    }
    return 0;
}
posted @ 2017-08-17 14:32  CzYoL  阅读(387)  评论(0编辑  收藏  举报