fhq treap

fhq treap(神仙数据结构)

前置知识:treap平衡树 BST

开始

首先说一下 这个东西可以搞一切bst,treap,splay所能搞的东西 这个东西的学名应该是叫做fhq treap,应该是treap的强化版。 整个数据结构中只有两个操作: 1.分离(split) 就是把一棵树分成两个树(有按排名分裂和权值分裂两种,下面都有代码) 2.合并(merge)把两棵树合成一棵树

操作

1.插入:

split(root,a,x,y);
root=merge(merge(x,new_node(a)),y);

这个比较好理解,我们先把树分为x,y两部分,然后把新的节点a看做是一棵树,先与x合并,合并完之后将合并的整体与y合并 2.删除

split(root,a,x,z);
split(x,a-1,x,y);
y=merge(ch[y][0],ch[y][1]);
root=merge(merge(x,y),z);

首先我们把树分为x和z两部分

那么x树中的最大权值为a

再把x分为x和y两部分。

此时x中的最大权值为a-1,且权值为a的节点一定是y的根节点。

然后我们可以无视y的根节点,直接把y的左右孩子合并起来,这样就成功的删除了根节点,

最后再把x,y,z合并起来就好

3.查询a的排名

 split(root,a-1,x,y);
printf("%d\n",siz[x]+1);
root=merge(x,y);

我们首先按照a-1的权值把树分开。

那么x树中最大的应该是a-1。

那么a的排名就是siz[x]+1

4.查询排名为a的数

1 printf(“%d\n”,val[kth(root,a)]);

直接调用查找排名的函数即可,

这个函数应该比较好理解。。 5.求x的前驱(前驱定义为小于a,且最大的数)

split(root,a-1,x,y);
printf("%d\n",val[kth(x,siz[x])]);
root=merge(x,y);

因为要小于a,那么我们按照a-1的权值划分,

x中最大的一定是<=a-1的,

所以我们直接输出x中最大的数就好,

(这里有一个小技巧,因为siz储存的是节点的数目,然后根据二叉查找树的性质,编号最大的就是值最大的)

6.求x的后继(后继定义为大于x,且最小的数)

split(root,a,x,y);
printf("%d\n",val[kth(y,1)]);
root=merge(x,y);

按排名分裂 当我们遍历到一个节点时,如果它的权值小于k,那么它的左子树会被分到左边的树里,然后我们遍历它的右儿子,如果大于k,则把它的右子树分到右边的树里。如果到达递归边界now==0怎么办呢?这里会有两种情况: 1.root=0(即第一次split),很明显要给x和y初始化,即x=y=0。 2.split到了叶子节点,此时无法继续split了,只能返回。此时的x=y=0没什么用,因为x和y会在回溯的时候通过地址符号改变。

void split(int now,int k,int &x,int &y)
{
    if(!now)
        x=y=0;
    else
    {
        if(k<=siz[ch[now][0]])
        {
            y=now;
            split(ch[now][0],k,x,ch[now][0]);
        }
        else
        {
            x=now;
            split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);
        }
        update(now);
    }
}

按权值分裂 与按权值分裂类似

void split(int now,int k,int &x,int &y)
{
    if(!now)
        x=y=0;
    else if(val[now]<=k)
    { 
        x=now;
        split(ch[now][1],k,ch[now][1],y);
    }
    else
    {
        y=now;
        split(ch[now][0],k,x,ch[now][0]);
    }
    update(now);//update(i)为更新size[i]大小的函数
}

完整fhq代码:

    # include <iostream>
    # include <cstdlib>
    using namespace std;
    struct node{
        int val,rnd,l,r,size;
    }tree[100010];int top,root;
    void pushup(int x){
        tree[x].size = tree[tree[x].l].size+tree[tree[x].r].size+1;
    }
    int newnode(int x){
        tree[++top].val = x;tree[top].size  = 1;tree[top].rnd = rand();
        return top;
    }
    int merge(int x,int y){//x<y
        if(!x||!y)return x+y;//已经空了
        if(tree[x].rnd<tree[y].rnd){
            tree[x].r = merge(tree[x].r,y);
            pushup(x);return x;
        }else {
            tree[y].l = merge(x,tree[y].l);
            pushup(y);return y;
        }
    }
    void split(int now,int k,int &x,int &y){//以x为权值分为两棵树
        if(!now) x=y=0;//
        else {
            if(tree[now].val<=k)
            x = now,split(tree[now].r,k,tree[now].r,y);
            else 
                y = now,split(tree[now].l,k,x,tree[now].l);
            pushup(now);
        }
    }
    int kth(int now,int k){
        while(true){
            if(k<=tree[tree[now].l].size){
                now = tree[now].l;
            }else {
                if(k==tree[tree[now].l].size+1)return now;
                else{
                   k-=tree[tree[now].l].size+1;
                   now = tree[now].r;
                }
            }
        }
    }
    int main(){
        srand(19260817);
        int T,op,x,y,z,a,b;cin>>T;
        while(T--){
            cin>>op>>a;
            if(op==1){
                split(root,a,x,y);
                root = merge(merge(x,newnode(a)),y);
            }else if(op==2){
                split(root,a,x,z);split(x,a-1,x,y);
                y = merge(tree[y].l,tree[y].r);
                root = merge(merge(x,y),z);
            }else if(op==3){
                split(root,a-1,x,y);
                cout<<tree[x].size+1<<endl;
                root = merge(x,y);
            }else if(op==4){
                cout<<tree[kth(root,a)].val<<endl;
            }else if(op==5){
                split(root,a-1,x,y);
                cout<<tree[kth(x,tree[x].size)].val<<endl;
                root = merge(x,y);
            }else if(op==6){
                split(root,a,x,y);
                cout<<tree[kth(y,1)].val<<endl;
                root = merge(x,y);
            }
        }
        return 0;
    }

文末彩蛋

treap模板

//
// Created by dhy on 18-12-2.
//
# include <cstdlib>
# include <cstdio>
int read(){
    int x = 0,f = 1;
    static char c = getchar();
    while(c<'0'||c>'9'){ if(c=='-')f = -1;c = getchar(); }
    while(c>='0'&&c<='9'){ x = (x<<1)+(x<<3)+c-'0';c = getchar(); }
    return x*f;
}
const int SIZE = 100010;
struct Treap{
    int l,r;
    int val,dat;//key code and weight
    int cnt,size;//
}tree[SIZE];
int tot,root,INF = 0x7fffffff;
int New(int val){
    tree[++tot].val = val;
    tree[tot].dat = rand();
    tree[tot].cnt = tree[tot].size = 1;
    return tot;
}
void update(int p){//左旋右旋以后,要更新一下
    tree[p].size = tree[tree[p].l].size+tree[tree[p].r].size+tree[p].cnt;
}
void build(){
    New(-INF),New(INF);
    root = 1;
    tree[1].r = 2;
    update(root);
}
int getRankByVal(int p,int val){
    if(p==0)return 0;//
    if(val==tree[p].val)return tree[tree[p].l].size+1;
    if(val<tree[p].val)return getRankByVal(tree[p].l,val);
    return getRankByVal(tree[p].r,val)+tree[tree[p].l].size+tree[p].cnt;
}
int getValByRank(int p,int rank){
    if(p==0)return INF;
    if(tree[tree[p].l].size>=rank)return getValByRank(tree[p].l,rank);
    if(tree[tree[p].l].size+tree[p].cnt>=rank)return tree[p].val;
    return getValByRank(tree[p].r,rank-tree[tree[p].l].size-tree[p].cnt);
}
void zig(int &p){//turn to right
    int q = tree[p].l;
    tree[p].l = tree[q].r,tree[q].r = p,p = q;
    update(tree[p].r);update(p);
}
void zag(int &p){//turn to left
    int q = tree[p].r;
    tree[p].r = tree[q].l;
    tree[q].l = p;
    p = q;
    update(tree[p].l);
    update(p);
}
void insert(int &p,int val){
    if(p==0){
        p = New(val);
        return;
    }
    if(val == tree[p].val){
        tree[p].cnt++;
        update(p);
        return;
    }
    if(val<tree[p].val){
        insert(tree[p].l,val);
        if(tree[p].dat<tree[tree[p].l].dat)zig(p);//就是插入节点以后,右旋上来
    }else{
        insert(tree[p].r,val);
        if(tree[p].dat<tree[tree[p].r].dat)zag(p);//插入到右子树,左旋上来
    }
    update(p);
}
int getPre(int val){
    int ans = 1;
    int p = root;
    while(p){
        if(val==tree[p].val){
            if(tree[p].l>0){
                p = tree[p].l;
                while(tree[p].r>0)p = tree[p].r;//找到val,然后从val的左节点开始,一路向右走
                ans = p;
            }
            break;
        }
        if(tree[p].val<val&&tree[p].val>tree[ans].val)ans = p;//找前驱,不断更新ans
        p = val<tree[p].val?tree[p].l:tree[p].r;//左走右走
    }
    return tree[ans].val;
}
int getNext(int val){
    int ans = 2;
    int p = root;
    while(p){
        if(val==tree[p].val){
            if(tree[p].r>0){
                p = tree[p].r;
                while(tree[p].l>0)p = tree[p].l;//后继,和上面一样的
                ans = p;
            }
            break;
        }
        if(tree[p].val>val&&tree[p].val<tree[ans].val)ans = p;
        p = val<tree[p].val?tree[p].l:tree[p].r;
    }
    return tree[ans].val;
}
void remove(int &p,int val){
    if(p==0)return;
    if(tree[p].val==val){
        if(tree[p].cnt>1){
            tree[p].cnt--;
            update(p);
            return;
        }
        if(tree[p].l||tree[p].r){//左右子树有一个不为0
            if(tree[p].r==0||tree[tree[p].l].dat>tree[tree[p].r].dat)
                zig(p),remove(tree[p].r,val);
            else
                zag(p),remove(tree[p].l,val);
            update(p);
        }
        else
            p = 0;
        return;
    }
    val<tree[p].val?remove(tree[p].l,val):remove(tree[p].r,val);
    update(p);
}
int main(){
    build();
    int n = read();
    while(n--){
        int opt,x;
        opt = read(),x = read();
        switch (opt){
            case 1:
                insert(root,x);
                break;
            case 2:
                remove(root,x);
                break;
            case 3:
                printf("%d\n",getRankByVal(root,x)-1);
                break;
            case 4:
                printf("%d\n",getValByRank(root,x+1));
                break;
            case 5:
                printf("%d\n",getPre(x));
                break;
            case 6:
                printf("%d\n",getNext(x));
                break;
        }
    }
    return 0;
}

splay 模板(无区间反转)

    # include <iostream>
    # include <cstdio>
    using namespace std;
    const int MAXN = 100010;
    int a[MAXN];
    int root;
    inline void swap(int &x,int &y){x = x^y;y = y^x;x = x^y;}
    int tree[MAXN][2];int sze[MAXN];int cnt[MAXN];
    int val[MAXN];int lzy[MAXN];int tot;int fa[MAXN];
    int n;
    inline void pushup(int x){sze[x] = sze[tree[x][0]]+sze[tree[x][1]]+cnt[x];}
    int which(int x){return tree[fa[x]][1]==x;}
    void rotate(int x){
        int y = fa[x];int z = fa[y],d = which(x),w = tree[x][d^1];
        tree[y][d] = w;fa[w] = y;
        tree[z][which(y)] = x;fa[x] = z;
        tree[x][d^1] = y;fa[y] = x;
        pushup(y);pushup(x);
    }
    void splay(int x,int tar = 0){
        while(fa[x]!=tar){
            int y = fa[x];int z = fa[y];
            if(z!=tar){
                if(which(x)==which(y))rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(!tar)root = x;
    }
    void pushdown(int x){
        if(lzy[x]){
            swap(tree[x][1],tree[x][0]);
            lzy[tree[x][0]]^= 1;lzy[tree[x][1]]^=1;
            lzy[x] = 0;
        }
    }
    int kth(int rank){
        int curr = root;
        while(1){
            pushdown(curr);
            if(tree[curr][0]&&rank<=sze[tree[curr][0]]){
                curr = tree[curr][0];
            }else if(rank>sze[tree[curr][0]]+cnt[curr]){
                rank-=sze[tree[curr][0]]+cnt[curr];
                curr = tree[curr][1];
            }else return curr;
        }
    }
    void insert(int x){
        int curr = root;int p = 0;
        while(curr&&val[curr]!=x){
            if(x>val[curr])p = curr,curr = tree[curr][1];else p = curr,curr = tree[curr][0];
        }
        if(curr){
            cnt[curr]++;
        }else{
            curr = ++tot;
            if(p)tree[p][x>val[p]] = curr;
            tree[curr][0] = tree[curr][1] = 0;
            fa[curr] = p;val[curr] = x;
            cnt[curr] = sze[curr] = 1;
        }
        splay(curr);
    }
    void find(int x){
        int curr = root;
        while(tree[curr][x>val[curr]]&&val[curr]!=x){
            curr = tree[curr][x>val[curr]];
        }
        splay(curr);
    }
    void reverse(int l,int r){
        int x = kth(l), y =kth(r+2);
        splay(x);
        splay(y,x);
        lzy[tree[y][0]]^=1;
    }
    int pre(int x){
        find(x);
        if(val[root]<x)return root;
        int curr = tree[root][0];
        while(tree[curr][1])curr = tree[curr][1];
        return curr;
    }
    int succ(int x){
        find(x);
        if(val[root]>x)return root;
        int curr = tree[root][1];
        while(tree[curr][0])curr = tree[curr][0];
        return curr;
    }
    void output(int x){
        pushdown(x);
        if(tree[x][0])output(tree[x][0]);
        if(val[x]&&val[x]<=n)printf("%d ",val[x]);
        if(tree[x][1])output(tree[x][1]);
    }
    int main(){
        int m;
        scanf("%d%d",&n,&m);
        int x,y;
        for(int i = 0;i<=n+1;i++)insert(i);
        while(m--){
            scanf("%d%d",&x,&y);
            reverse(x,y);
        }
        output(root);
        return 0;
    }

参考文献

Chanis博客传送门 范浩强 浅谈数据结构地址