【模板】FHQ Treap

FHQ Treap


无旋Treap

维护的信息

c h [ i ] [ 0 / 1 ] ch[i][0/1] ch[i][0/1] v a l [ i ] val[i] val[i] f i x [ i ] fix[i] fix[i] s i z [ i ] siz[i] siz[i] t o t tot tot
左右儿子权值随机修正权值 i i i 为根的子树节点数节点总数
r t rt rt x x x y y y z z z
根节点编号临时子树编号临时子树编号临时子树编号

类定义

class FHQ_Treap {
private:
#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
    int ch[maxn][2], val[maxn], fix[maxn], siz[maxn], tot;
    int rt, x, y, z;
public:
}T;

操作

基本操作

m a i n t a i n ( x ) maintain(x) maintain(x) : 更新节点 x x x s i z siz siz

n e w ( v ) new(v) new(v) : 新建一个权值为 v v v 的节点

void maintain(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + 1; }
int New(int v) {
    val[++tot] = v;
    fix[tot] = rand();
    siz[tot] = 1;
    return tot;
}

核心操作

分裂

S p l i t ( i , k , x , y ) Split(i,k,x,y) Split(i,k,x,y) : 当前节点为 i i i ,按权值 k k k 将其分为 x x x y y y 两个子树, x x x y y y 为两个子树的根节点编号 (注: x x x y y y 需要加引用,这样分裂操作才可以修改 c h ch ch 来做到正确维护)

  • 若当前节点为 0 0 0 ,则说明为空,令 x = y = 0 x = y =0 x=y=0 ,返回
  • 当前节点 v a l < = k val <= k val<=k ,则其左子树都可以放在 x x x 上,继续在右子树中寻找
  • 当前节点 v a l > k val > k val>k ,则其右子树都可以放在 y y y 上,继续在左子树中寻找
  • 寻找完后维护子树大小
void Split(int i, int k, int& x, int& y) {
    if(!i) x = y = 0;
    else {
        if(val[i] <= k) {
            x = i;
            Split(rs(i), k, rs(i), y);
        }
        else {
            y = i;
            Split(ls(i), k, x, ls(i));
        }
        maintain(i);
    }
}
合并

M e r g e ( x , y ) Merge(x,y) Merge(x,y) : 将子树 x x x y y y 合并,并返回根节点编号(其中 x x x 子树的值小于等于 y y y 子树)

  • 若有一个子树为空,则返回 x   ∣   y x\,|\,y xy ,及非空的子树
  • 通过比较 x x x y y y f i x fix fix 值,确定位置
int Merge(int x, int y) {
    if(!x || !y) return x | y;
    if(fix[x] > fix[y]) { //在堆中,x 在 y 的上方,值小于 y ,所以在 y 的左上方
        rs(x) = Merge(rs(x), y); // x 的右子树与 y 合并
        maintain(x);
        return x;
    }
    else { //在堆中,x 在 y 的下方,值小于 y ,所以在 y 的左下方
        ls(y) = Merge(x, ls(y)); // x 和 y 的左子树合并
        maintain(y);
        return y;
    }
}

插入操作

i n s e r t ( v ) insert(v) insert(v) : 插入一个权值为 v v v 的节点

  • v v v 将树分裂
  • x x x 与新节点合并,再与 y y y 合并
void insert(int v) {
    Split(rt, v, x, y);
    rt = Merge(Merge(x, New(v)), y);
}

删除操作

d e l ( v ) del(v) del(v) : 删除权值为 v v v 的节点

  • 分裂两次,一次按 v v v ,一次按 v − 1 v - 1 v1
  • y y y 的根删除(将左右子树合并即可)
  • 将三颗子树合并
void del(int v) {
    Split(rt, v, x, z);
    Split(x, v - 1, x, y);
    y = Merge(ls(y), rs(y));
    rt = Merge(Merge(x, y), z);
}

查询排名

r a n k ( v ) rank(v) rank(v) : 查询值 v v v 的排名

  • v − 1 v - 1 v1 分裂,子树 x x x 的大小 + 1 +1 +1 即为答案(不可按 v v v 划分,可能有重复)
int rank(int v) {
    Split(rt, v - 1, x, y);
    int ret = siz[x] + 1;
    rt = Merge(x, y);
    return ret;
}

按排名查值,前驱,后继

k t h ( x , v ) kth(x,v) kth(x,v) : 返回子树 x x x 中排名为 v v v 的下标

int kth(int x, int k) {
    while(1) {
        if(k <= siz[ls(x)])
            x = ls(x);
        else if(k == siz[ls(x)] + 1)
            return x;
        else k -= siz[ls(x)] + 1, x = rs(x);
    }
}

K t h ( k ) Kth(k) Kth(k) : 排名为 k k k 的数

int Kth(int k) {
    return val[kth(rt, k)];
}

p r e ( v ) pre(v) pre(v) : 值 v v v 的前驱

  • 按照 v − 1 v - 1 v1 分裂, x x x 中排名最大的节点即为 v v v 的前驱
int pre(int v) {
    Split(rt, v - 1, x, y);
    int ret = val[kth(x, siz[x])];
    rt = Merge(x, y);
    return ret;
}

n x t ( v ) nxt(v) nxt(v) : 值 v v v 的后继

  • 按照 v v v 分裂, y y y 中排名最小的节点即为 v v v 的后继
int nxt(int v) {
    Split(rt, v, x, y);
    int ret = val[kth(y, 1)];
    rt = Merge(x, y);
    return ret;
}

【模板】普通平衡树
【模板】普通平衡树(数据加强版)

#include <bits/stdc++.h>
#define maxn 1000005
using namespace std;
int read() {
    int ret = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        ret = (ret << 1) + (ret << 3) + (ch ^ '0');
        ch = getchar();
    }
    return ret * f;
}
class FHQ_Treap {
private:
#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
    int ch[maxn][2], val[maxn], fix[maxn], siz[maxn], tot;
    int rt, x, y, z;
    void maintain(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + 1; }
    int New(int v) {
        val[++tot] = v;
        fix[tot] = rand();
        siz[tot] = 1;
        return tot;
    }
    void Split(int i, int k, int& x, int& y) {
        if (!i) x = y = 0;
        else {
            if (val[i] <= k) {
                x = i;
                Split(rs(i), k, rs(i), y);
            }
            else {
                y = i;
                Split(ls(i), k, x, ls(i));
            }
            maintain(i);
        }
    }
    int Merge(int x, int y) {
        if (!x || !y) return x | y;
        if (fix[x] < fix[y]) {
            rs(x) = Merge(rs(x), y);
            maintain(x);
            return x;
        }
        else {
            ls(y) = Merge(x, ls(y));
            maintain(y);
            return y;
        }
    }
public:
    void insert(int v) {
        Split(rt, v, x, y);
        rt = Merge(Merge(x, New(v)), y);
    }
    void del(int v) {
        Split(rt, v, x, z);
        Split(x, v - 1, x, y);
        y = Merge(ls(y), rs(y));
        rt = Merge(Merge(x, y), z);
    }
    int kth(int x, int k) {
        while (1) {
            if (k <= siz[ls(x)])
                x = ls(x);
            else if (k == siz[ls(x)] + 1)
                return x;
            else k -= siz[ls(x)] + 1, x = rs(x);
        }
    }
    int rank(int v) {
        Split(rt, v - 1, x, y);
        int ret = siz[x] + 1;
        rt = Merge(x, y);
        return ret;
    }
    int Kth(int k) {
        return val[kth(rt, k)];
    }
    int pre(int v) {
        Split(rt, v - 1, x, y);
        int ret = val[kth(x, siz[x])];
        rt = Merge(x, y);
        return ret;
    }
    int nxt(int v) {
        Split(rt, v, x, y);
        int ret = val[kth(y, 1)];
        rt = Merge(x, y);
        return ret;
    }
}T;
int n, opt, x;
int main() {
    srand(time(NULL));
    n = read();
    for (int i = 1; i <= n; i++) {
        opt = read(), x = read();
        if (opt == 1) T.insert(x);
        if (opt == 2) T.del(x);
        if (opt == 3) cout << T.rank(x) << endl;
        if (opt == 4) cout << T.Kth(x) << endl;
        if (opt == 5) cout << T.pre(x) << endl;
        if (opt == 6) cout << T.nxt(x) << endl;
    }
    return 0;
}

可持久化

让FHQ Treap可持久化,只需要在操作时复制节点,以便不影响之前的版本

复制节点

c o p y n o d e ( x ) copynode(x) copynode(x) : 复制节点 x x x

int copynode(int x) {
    val[++tot] = val[x]; siz[tot] = siz[x]; fix[tot] = fix[x];
    ls(tot) = ls(x); rs(tot) = rs(x);
    return tot;
}
void split(int i, int k, int& x, int& y) {
    if (!i)x = y = 0;
    else {
        if (val[i] <= k) {
            x = copynode(i);
            split(rs(x), k, rs(x), y);
            maintain(x);
        }
        else {
            y = copynode(i);
            split(ls(y), k, x, ls(y));
            maintain(y);
        }
    }
}
int Merge(int x, int y) {
    if (!x || !y)return x | y;
    if (fix[x] < fix[y]) {
        int p = copynode(x);
        rs(p) = Merge(rs(p), y);
        maintain(p); return p;
    }
    else {
        int p = copynode(y);
        ls(p) = Merge(x, ls(p));
        maintain(p); return p;
    }
}

r t → r t [ i ] rt \rightarrow rt[i] rtrt[i] 来记录不同版本的根节点

【模板】可持久化平衡树

#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
inline int read() {
    int ret = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        ret = (ret << 1) + (ret << 3) + (ch ^ '0');
        ch = getchar();
    }
    return ret * f;
}
int rt[maxn];
class FHQ_Treap {
private:
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
    int ch[maxn * 50][2];
    int siz[maxn * 50], fix[maxn * 50], val[maxn * 50];
    int tot;
    inline int copynode(int x) {
        siz[++tot] = siz[x]; fix[tot] = fix[x]; val[tot] = val[x];
        ch[tot][0] = ch[x][0]; ch[tot][1] = ch[x][1];
        return tot;
    }
    inline void maintain(int x) {
        siz[x] = siz[ls(x)] + siz[rs(x)] + 1;
    }
    inline int New(int x) {
        val[++tot] = x; siz[tot] = 1; fix[tot] = rand();
        return tot;
    }
    inline int Merge(int x, int y) {
        if (!x || !y)return x | y;
        if (fix[x] < fix[y]) {
            int p = copynode(x);
            rs(p) = Merge(rs(p), y);
            maintain(p); return p;
        }
        else {
            int p = copynode(y);
            ls(p) = Merge(x, ls(p));
            maintain(p); return p;
        }
    }
    inline void split(int i, int k, int& x, int& y) {
        if (!i)x = y = 0;
        else {
            if (val[i] <= k) {
                x = copynode(i);
                split(rs(x), k, rs(x), y);
                maintain(x);
            }
            else {
                y = copynode(i);
                split(ls(y), k, x, ls(y));
                maintain(y);
            }
        }
    }
public:
    inline void Delete(int& root, int v) {
        int x = 0, y = 0, z = 0;
        split(root, v, x, z);
        split(x, v - 1, x, y);
        y = Merge(ls(y), rs(y));
        root = Merge(Merge(x, y), z);
    }
    inline void Insert(int& root, int v) {
        int x = 0, y = 0, z = 0;
        split(root, v, x, y);
        root = Merge(Merge(x, New(v)), y);
    }
    inline int kth(int k, int v) {
        if (v == siz[ls(k)] + 1)return val[k];
        else if (v <= siz[ls(k)])return kth(ls(k), v);
        else return kth(rs(k), v - siz[ls(k)] - 1);
    }
    inline int rank(int& root, int v) {
        int x, y;
        split(root, v - 1, x, y);
        int ans = siz[x] + 1;
        root = Merge(x, y);
        return ans;
    }
    inline int pre(int& root, int v) {
        int x, y, k, ans;
        split(root, v - 1, x, y);
        if (!x)return -2147483647;
        k = siz[x];
        ans = kth(x, k);
        root = Merge(x, y);
        return ans;
    }
    inline int nex(int& root, int w) {
        int x, y, ans;
        split(root, w, x, y);
        if (!y)return 2147483647;
        else ans = kth(y, 1);
        root = Merge(x, y);
        return ans;
    }
}T;
int n;
int main() {
    n = read();
    for (register int i = 1, opt, v, tim; i <= n; ++i) {
        tim = read(), opt = read(), v = read();
        rt[i] = rt[tim];
        if (opt == 1) T.Insert(rt[i], v);
        else if (opt == 2) T.Delete(rt[i], v);
        else if (opt == 3) printf("%d\n", T.rank(rt[i], v));
        else if (opt == 4) printf("%d\n", T.kth(rt[i], v));
        else if (opt == 5) printf("%d\n", T.pre(rt[i], v));
        else printf("%d\n", T.nex(rt[i], v));
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值