1. 程式人生 > 其它 >P3224 [HNOI2012]永無鄉(fhq-treap題解)

P3224 [HNOI2012]永無鄉(fhq-treap題解)

\(《是的我又回來了》\)
題目連結:永無鄉
思路:利用無旋\(treap\)。先看操作\(B\),(因為你不能夠保證一個樹中的所有元素都小於另一顆樹,所以不能通過merge函式將兩樹合併)可以這樣處理,我們考慮兩個\(fhq-treap\)的合併,可以參考啟發式合併的思路,暴力的遍歷一個較小的集合,並將其加入大的集合中。啟發式合併的複雜度是均攤\(\Theta (nlogn)\)。然後再看操作\(Q\),那就是經典\(fhq-treap\)找第\(k\)大的思路。
對於快速判斷屬於哪一個集合,利用並查集。
\(Code\)

const int N = 4e5+10;
int fa[N],sz[N];
int found(int x){
    if(fa[x] == x)return fa[x];
    return fa[x] = found(fa[x]);
}
struct node {
    int l,r;
    int key;
    int val,tag; 
    int sz; 
}tr[N];
int tot ,res ,n,m;
int a[N];
mt19937 rnd(2884);
inline int newnode(int va){
    tr[++tot].val = va;
    tr[tot].sz = 1;
    tr[tot].key = rnd();
    return tot;
}
int root[N] ;
#define lson tr[p].l
#define rson tr[p].r
inline void pushup(int p){
    tr[p].sz = tr[lson].sz + tr[rson].sz + 1;
}
void split(int p,int k,int &x,int &y){
    if(!p)x = y = 0;
    else {
        if(tr[p].val <= k){
            x = p;
            split(rson,k,rson,y);
        }else {
            y = p;
            split(lson,k,x,lson);
        }
        pushup(p);
    }
}

int merge(int x,int y){
    if(!x or !y)return x + y ;
    if(tr[x].key > tr[y].key){
        tr[x].r = merge(tr[x].r,y);
        pushup(x);
        return x;
    }else {
        tr[y].l = merge(x,tr[y].l);
        pushup(y);
        return y;
    }
}
int idx ;
inline void ins(int va){
    int x,y,z;
    split(root[idx],va,x,y);
    //cout << va <<"!"<< endl;
    int now = newnode(va);
    x = merge(x,now);
    root[idx] = merge(x,y);
    
}

void dfs(int id){
    if(!id)return ;
    dfs(tr[id].l);
    ins(tr[id].val);
    dfs(tr[id].r);
}

void Merge(int x,int y){
    //if(x == y)return ;
    idx = x;
    dfs(root[y]);
    sz[x] += sz[y];
    fa[y] = x;
}
int h[N];
void split_sz(int p,int k,int &x,int &y){
    if(!p)x = y = 0;
    else {
        if(tr[lson].sz + 1 <= k){
            x = p;
            split_sz(rson,k-tr[lson].sz-1,rson,y);
        }else {
            y = p;
            split_sz(lson,k,x,lson);
        }
        pushup(p);
    }
}
void out(int x){
    if(!x)return ;
    out(tr[x].l);
    cout << tr[x].val << ' ';
    out(tr[x].r);
}
void query(int id,int k){
    int now = found(id);
    //cout <<sz[now]<<"@"<<endl;
    if(sz[now] < k){
        puts("-1");return ;
    }
//    k = sz[now] - k + 1;
    int x,y,z;
    split_sz(root[now],k,x,y);
    //cout <<k <<"??"<< x <<"!!"<<y<<endl;
    int idx = x;
    while(tr[idx].r)idx = tr[idx].r;
    write(h[tr[idx].val]);pc('\n');
    root[now] = merge(x,y);
}
void solve(){
    read(n);read(m);
    rep(i,1,n){
        int t ;
        read(t);a[i] = t;h[t] = i;
    }
    rep(i,1,n){
        fa[i] = i;sz[i] = 1;
        root[i] = newnode(a[i]);
    }
    //cout <<tr[root[2]].val<<endl;
    rep(i,1,m){
        int u,v;
        read(u);read(v);
        int p = found(u),q = found(v);
        if(p != q){
            if(sz[p] < sz[q])swap(p,q);
            Merge(p,q);
        }
    }
    int idx = found(1);
    //out(root[idx]);cout <<endl;
    int q;
    read(q);
    while(q--){
        char op[33];
        int x,y;
        scanf("%s%d%d",op,&x,&y);
        if(*op == 'Q'){
            query(x,y);
        }else {
            int p = found(x),q = found(y);
            if(p==q)continue;
            if(sz[p] < sz[q])swap(p,q);
            Merge(p,q);
        }
        int now = found(1);
        //cout<<x<<' '<<y<<"::::";
        //out(root[now]);cout <<endl;
    }
}


signed main(){
    solve();
}