☆ [HNOI2012] 永无乡 「平衡树启发式合并」

题目类型:平衡树启发式合并

传送门:>Here<

题意:节点可以连边(不能断边),询问任意两个节点的连通性与一个连通块中排名第\(k\)的节点

解题思路

如果不需要询问排名,那么并查集即可。如果只询问排名第一,那么左偏树即可。现在要询问排名第\(k\)小,就需要用平衡树来解决

平衡树求解排名第\(k\)是轻而易举的,然而怎么合并两棵平衡树呢?

启发式合并。所谓启发式合并,就是暴力合并……

所谓启发式合并(不仅仅是平衡树),就是比较要合并的两个结构,选择较小的那一个结构,将其中节点一个一个拆下来插入到较大的那个结构中去。因此当我们合并两棵平衡树时,将\(size\)较小的那一颗平衡树中的节点一个一个拆下来插入到较大的那棵平衡树上。

按什么顺序拆呢?如果每次选择根节点删除然后插入显得很愚蠢。我们尽可能优化删除的情况(插入不可能优化了吧……)——按照后序遍历的顺序来插入。这样的话当我插入一个节点时,它的左右子树肯定都已经没了,因此只需要简单地将自己删除就好了。

Code

/*By DennyQi 2018*/
#include 
#include 
#include 
#include 
#define  r  read()
using namespace std;
typedef long long ll;
const int MAXN = 100010;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int N,M,q,A,B,x,y; char opt[10];
int val[MAXN],bel[MAXN],rt[MAXN];
struct Splay{
    int ch[MAXN][2],fa[MAXN],size[MAXN];
    inline bool rson(int f, int x){
        return ch[f][1] == x;
    }
    inline void update(int x){
        size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
    }
    inline void rotate(int x){
        int f = fa[x], gf = fa[f];
        int p = rson(f, x), q = !p;
        if(!gf) rt[bel[x]] = x; else ch[gf][rson(gf,f)] = x;
        fa[x] = gf;
        ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
        ch[x][q] = f, fa[f] = x;
        update(f), update(x);
    }
    inline void splay(int x, int target){
        int f,gf;
        while(fa[x] != target){
            f = fa[x], gf = fa[f];
            if(gf == target){
                rotate(x); break;
            }
            if(rson(gf,f) ^ rson(f,x)) rotate(x); else rotate(f);
            rotate(x);
        }
    }
    inline void insert(int A, int x){
        int o = rt[A];
        while(o){
            bool b = val[x] > val[o];
            if(!ch[o][b]){
                ch[o][b] = x;
                fa[x] = o;
                bel[x] = A;
                size[x] = 1;
                splay(x, 0);
                break;
            }
            o = ch[o][b];
        }
    }
    void HEmerge(int A, int x){
        if(ch[x][0]) HEmerge(A, ch[x][0]);
        if(ch[x][1]) HEmerge(A, ch[x][1]);
        insert(A, x);
    }
    inline void merge(int A, int B){
        if(size[rt[A]] < size[rt[B]]) swap(A, B);
        HEmerge(A, rt[B]);
        rt[B] = 0;
    }
    inline int query(int A, int k){
        int o = rt[A];
        if(size[o] < k) return -1;
        while(o){
            if(size[ch[o][0]] >= k){
                o = ch[o][0];
            }
            else if(size[ch[o][0]] + 1 < k){
                k -= size[ch[o][0]] + 1;
                o = ch[o][1];
            }
            else{
                return o;
            }
        }
    }
}qxz;
int main(){
    N = r, M = r;
    for(int i = 1; i <= N; ++i){
        val[i] = r;
        rt[i] = i;
        bel[i] = i;
        qxz.size[i] = 1;
    }
    for(int i = 1; i <= M; ++i){
        A = r, B = r;
        if(bel[A] != bel[B]) qxz.merge(bel[A], bel[B]);
    }
    scanf("%d", &q);
    while(q--){
        scanf("%s %d %d", opt, &x, &y);
        if(opt[0] == 'B'){
            if(bel[x] == bel[y]) continue;
            qxz.merge(bel[x], bel[y]);
        }
        else{
            printf("%d\n", qxz.query(bel[x], y));
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/qixingzhi/p/9656031.html

你可能感兴趣的:(☆ [HNOI2012] 永无乡 「平衡树启发式合并」)