「FHQ Treap」学习笔记

话说天下大事,就像fhq treap —— 分久必合,合久必分


简单讲一讲。非旋treap主要依靠分裂和合并来实现操作。(递归,不维护fa不维护cnt)

合并的前提是两棵树的权值满足一边的最大的比另一边最小的还小。因此时合并时只需要维护键值的堆性质即可。这样每一次比较根节点,如果x比y小那么y直接接到x的右子树即可(需要满足权值的平衡树性质);否则的话只需要反过来,把x接到y的左子树上。merge函数返回的值应当是合并完后的根节点。

分裂分为两种,排名和权值。然而我认为它们本质上是一样的。对于权值的分裂,对于每一个子树的根节点,若根节点比给定值a小,那么此节点及左子树一定比a小,归入x。否则此节点及右子树归入y。然后再递归操作还没有分类的那一个子树就好了。代码实现中的引用用的非常巧妙。可以把这里的引用理解为是需要修改的东西,利用递归的过程对其作出修改。

合并看键值,分裂看权值。


$Merge$

int merge(int u, int v){
    if(!u||!v) return u|v;
    if(key[u] < key[v]){
        ch[u][1] = merge(ch[u][1], v);
        pushup(u); 
        return u;
    }
    else{
        ch[v][0] = merge(u, ch[v][0]);
        pushup(v); 
        return v;
    }
}

$Split$

void split_a(int u, int a, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(val[u] <= a){
        x = u;
        split_a(ch[u][1], a, ch[u][1], y);
    }
    else{
        y = u;
        split_a(ch[u][0], a, x, ch[u][0]);
    }
    pushup(u);
}
void split_k(int u, int k, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(size[ch[u][0]]+1 <= k){
        x = u;
        split_k(ch[u][1], k-size[ch[u][0]]-1, ch[u][1], y);
    }
    else{
        y = u;
        split_k(ch[u][0], k, x, ch[u][0]);
    }
    pushup(u);
}

有了这两个操作,其他操作yy一下即可,非常简便。

当然,在查询k大排名或前驱后继时,完全可以用普通平衡树(如splay)的做法。因为它满足平衡树的性质。也就是傻乎乎的去logn的从根节点往下走。那么既然我们有了split,查询最大最小值应该很方便了。要尽量让代码精简啊!

My Code:

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;
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,opt,x,num_node,RooT;
int ch[MAXN][2],val[MAXN],key[MAXN],size[MAXN];
inline void pushup(int x){
    size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
inline void clear(int x){
    size[x]=val[x]=key[x]=ch[x][0]=ch[x][1]=0;
}
int merge(int u, int v){
    if(!u||!v) return u|v;
    if(key[u] < key[v]){
        ch[u][1] = merge(ch[u][1], v);
        pushup(u); 
        return u;
    }
    else{
        ch[v][0] = merge(u, ch[v][0]);
        pushup(v); 
        return v;
    }
}
void split_a(int u, int a, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(val[u] <= a){
        x = u;
        split_a(ch[u][1], a, ch[u][1], y);
    }
    else{
        y = u;
        split_a(ch[u][0], a, x, ch[u][0]);
    }
    pushup(u);
}
void split_k(int u, int k, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(size[ch[u][0]]+1 <= k){
        x = u;
        split_k(ch[u][1], k-size[ch[u][0]]-1, ch[u][1], y);
    }
    else{
        y = u;
        split_k(ch[u][0], k, x, ch[u][0]);
    }
    pushup(u);
}
inline void Insert(int v){//以v进行分裂,插入后合并。 
    val[++num_node]= v;
    key[num_node] = rand();
    size[num_node] = 1;
    int a,b;
    split_a(RooT, v, a, b);
    RooT = merge(merge(a,num_node), b);
}
inline void Delete(int v){//以v进行分裂,删除后合并。 
    int a,b,c,d;
    split_a(RooT, v, a, b);
    split_k(a, size[a]-1, c, d);
    clear(d);
    RooT = merge(c, b);
}
inline int Rank(int v){//以v-1进行分裂,看左侧树的size 
    int a,b,ans;
    split_a(RooT, v-1, a, b);
    ans = size[a]+1;
    RooT = merge(a, b);
    return ans;
}
inline int Kth(int k){//以k进行分裂,依旧看左侧的size,但注意再分裂一次取出比k小的 
    int a,b,c,d,ans;
    split_k(RooT, k, a, b);
    split_k(a,size[a]-1,c,d);
    ans = val[d];
    RooT = merge(merge(c,d),b);
    return ans;
}
inline int Pre(int v){//与kth同理 
    int a,b,c,d,ans;
    split_a(RooT, v-1, a, b);
    split_k(a,size[a]-1,c,d);
    ans = val[d];
    RooT = merge(merge(c,d),b);
    return ans;
}
inline int Nxt(int v){
    int a,b,c,d,ans;
    split_a(RooT, v, a, b);
    split_k(b, 1, c, d);
    ans = val[c];
    RooT = merge(a,merge(c,d));
    return ans;
}
void PrintTree(int u){
    if(ch[u][0]) PrintTree(ch[u][0]);
    printf("%d ",val[u]);
    if(ch[u][1]) PrintTree(ch[u][1]);
}
int main(){
//    freopen(".in","r",stdin);
    srand((unsigned)time(NULL));
    N = read();
    while(N--){
        opt = read(), x = read();
        if(opt == 1) Insert(x);
        if(opt == 2) Delete(x);
        if(opt == 3) printf("%d\n", Rank(x));
        if(opt == 4) printf("%d\n", Kth(x));
        if(opt == 5) printf("%d\n", Pre(x));
        if(opt == 6) printf("%d\n", Nxt(x));
    }
    return 0;
}

 

posted @ 2018-11-23 22:27  DennyQi  阅读(248)  评论(1编辑  收藏  举报