【模板】Treap & Cartesian tree

Treap \text{Treap} Treap

简介

Treap \text{Treap} Treap 是一种平衡树但是节点没有 parent \text{parent} parent 域(其实可以有,但是这里没必要),对于插入节点和普通的搜索树一样,在插入完成后要检查优先级是否满足大根堆的性质,如果新插入的节点不满足这个性质就旋转,将优先级高的转上来,删除节点分为两种情况,一种是还没找到要删除的节点的时候,就递归地寻找,如果找到了,调用另一个函数,删除一个 Treap \text{Treap} Treap 的根节点(对于一个 Treap \text{Treap} Treap 的根节点来说他的左右孩子要么是空,要么也是一棵 Treap \text{Treap} Treap ),将左右孩子中优先级较高的旋转上来,然后递归地去删除被旋转下去的那个原先的根节点,直到被旋转到叶子节点,就直接删除。

结构

template <typename T> struct Node {
    Node *lc, *rc;
    T key;
    int priority;
};

对于关键词, Treap \text{Treap} Treap 是一棵二叉搜索树,对于优先级(随机分配), Treap \text{Treap} Treap 满足大根堆的性质,为了方便,所有空指针可以设置为优先级 − ∞ -\infty 的节点。

分裂

插入一个优先级为 + ∞ +\infty + 的节点,关键词设置为 x x x ,那么根节点的左右两棵 Treap \text{Treap} Treap 的所有关键词分别为小于 x x x 和大于 x x x (不允许重复关键词的情况)。

合并

合并两棵 Treap \text{Treap} Treap 要求左边的树的所有关键词都小于右边的树,随便设置一个节点 x x x 使 x . left x.\text{left} x.left x . right x.\text{right} x.right 分别为两棵 Treap \text{Treap} Treap ,然后删除根节点。

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

数组的简化实现。。用了 mt19937 32 位梅森缠绕器。

#include 
using namespace std;
using ll = long long;
const int N = 1100005;
mt19937 gen(time(NULL));
struct Treap {
    static inline int ch[N][2], pri[N], val[N], siz[N], cnt[N], node_tot;
    int root;
    Treap() : root(0) { pri[0] = INT_MIN; }
    void pushup(int x) { siz[x] = siz[ch[x][0]] + cnt[x] + siz[ch[x][1]]; }
    int newnode(int v) {
        pri[++node_tot] = gen();
        val[node_tot] = v;
        siz[node_tot] = cnt[node_tot] = 1;
        return node_tot;
    }
    void rotate(int &x, int d) { // d=0左旋,d=1右旋
        int t = ch[x][1 ^ d];
        ch[x][1 ^ d] = ch[t][d], ch[t][d] = x;
        siz[t] = siz[x];
        pushup(x);
        x = t;
    }
    void insert(int &x, int v) {
        if (!x) {
            x = newnode(v);
        } else if (val[x] == v) {
            ++cnt[x], ++siz[x];
        } else {
            ++siz[x];
            int t = v > val[x];
            insert(ch[x][t], v);
            if (pri[ch[x][t]] > pri[x]) rotate(x, 1 ^ t); // 维持大根堆特性
        }
    }
    void delroot(int &x) {
        if (ch[x][0] == ch[x][1]) {
            x = 0;
        } else {
            int t = pri[ch[x][0]] > pri[ch[x][1]]; // 左边的优先级>右边的优先级就右旋
            rotate(x, t);
            delroot(ch[x][t]); // 如果前面是右旋的话:x已经变成左边的节点了,x的右孩子就是原先的节点
        }
    }
    void del(int &x, int v) {
        if (val[x] == v) {
            --siz[x];
            if (!--cnt[x]) delroot(x); // 出现次数为0了就删掉这个节点
        } else {
            --siz[x];
            del(ch[x][v > val[x]], v);
        }
    }
    int rank(int v) {
        int ans = 0, x = root;
        while (x) {
            if (v > val[x]) {
                ans += siz[ch[x][0]] + cnt[x];
                x = ch[x][1];
            } else if (v == val[x]) {
                ans += siz[ch[x][0]];
                break;
            } else {
                x = ch[x][0];
            }
        }
        return ans;
    }
    int kth(int k) {
        int x = root;
        while (k <= siz[ch[x][0]] || k > siz[ch[x][0]] + cnt[x]) {
            if (k <= siz[ch[x][0]]) {
                x = ch[x][0];
            } else {
                k -= siz[ch[x][0]] + cnt[x], x = ch[x][1];
            }
        }
        return val[x];
    }
    int pred(int v) {
        int x = root, ans = -0x3f3f3f3f;
        while (x) {
            if (val[x] < v) {
                ans = val[x];
                x = ch[x][1];
            } else {
                x = ch[x][0];
            }
        }
        return ans;
    }
    int succ(int v) {
        int x = root, ans = 0x3f3f3f3f;
        while (x) {
            if (val[x] > v) {
                ans = val[x];
                x = ch[x][0];
            } else {
                x = ch[x][1];
            }
        }
        return ans;
    }
} t;
int main() {
#ifdef LOCAL
    freopen("..\\in", "r", stdin), freopen("..\\out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m, v;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> v;
        t.insert(t.root, v);
    }
    int last = 0, ans = 0, cmd;
    while (m--) {
        cin >> cmd >> v;
        v ^= last;
        switch (cmd) {
        case 1: // 插入 xx 数
            t.insert(t.root, v);
            break;
        case 2: // 删除 xx 数(若有多个相同的数,只删除一个)
            t.del(t.root, v);
            break;
        case 3: // 查询 xx 数的排名(排名定义为比当前数小的数的个数 +1 )
            ans ^= (last = t.rank(v) + 1);
            break;
        case 4: // 查询排名为 xx 的数
            ans ^= (last = t.kth(v));
            break;
        case 5: // 求 xx 的前驱(前驱定义为小于 xx,且最大的数)
            ans ^= (last = t.pred(v));
            break;
        case 6: // 求 xx 的后继(后继定义为大于 xx,且最小的数)
            ans ^= (last = t.succ(v));
            break;
        }
    }
    cout << ans;
    return 0;
}

题目:P3380 【模板】二逼平衡树(树套树)

比以前减少了一些常数。

#include 
using namespace std;
using ll = long long;
const int N = 5e4 + 5, INF = INT_MAX;
mt19937 gen(time(NULL));
struct Treap {
    static inline int ch[N << 7][2], pri[N << 7], val[N << 7], siz[N << 7], cnt[N << 7], node_tot;
    int root;
    Treap() : root(0) { pri[0] = INT_MIN; }
    void pushup(int x) { siz[x] = siz[ch[x][0]] + cnt[x] + siz[ch[x][1]]; }
    int newnode(int v) {
        pri[++node_tot] = gen();
        val[node_tot] = v;
        siz[node_tot] = cnt[node_tot] = 1;
        return node_tot;
    }
    void rotate(int &x, int d) { // d=0左旋,d=1右旋
        int t = ch[x][1 ^ d];
        ch[x][1 ^ d] = ch[t][d], ch[t][d] = x;
        siz[t] = siz[x];
        pushup(x);
        x = t;
    }
    void insert(int &x, int v) {
        if (!x) {
            x = newnode(v);
        } else if (val[x] == v) {
            ++cnt[x], ++siz[x];
        } else {
            ++siz[x];
            int t = v > val[x];
            insert(ch[x][t], v);
            if (pri[ch[x][t]] > pri[x]) rotate(x, 1 ^ t); // 维持大根堆特性
        }
    }
    void delroot(int &x) {
        if (ch[x][0] == ch[x][1]) {
            x = 0;
        } else {
            int t = pri[ch[x][0]] > pri[ch[x][1]]; // 左边的优先级>右边的优先级就右旋
            rotate(x, t);
            delroot(ch[x][t]); // 如果前面是右旋的话:x已经变成左边的节点了,x的右孩子就是原先的节点
        }
    }
    void del(int &x, int v) {
        if (val[x] == v) {
            --siz[x];
            if (!--cnt[x]) delroot(x); // 出现次数为0了就删掉这个节点
        } else {
            --siz[x];
            del(ch[x][v > val[x]], v);
        }
    }
    int rank(int v) {
        int ans = 0, x = root;
        while (x) {
            if (v > val[x]) {
                ans += siz[ch[x][0]] + cnt[x];
                x = ch[x][1];
            } else if (v == val[x]) {
                ans += siz[ch[x][0]];
                break;
            } else {
                x = ch[x][0];
            }
        }
        return ans;
    }
    int kth(int k) {
        int x = root;
        while (k <= siz[ch[x][0]] || k > siz[ch[x][0]] + cnt[x]) {
            if (k <= siz[ch[x][0]]) {
                x = ch[x][0];
            } else {
                k -= siz[ch[x][0]] + cnt[x], x = ch[x][1];
            }
        }
        return val[x];
    }
    int pred(int v) {
        int x = root, ans = -INF;
        while (x) {
            if (val[x] < v) {
                ans = val[x];
                x = ch[x][1];
            } else {
                x = ch[x][0];
            }
        }
        return ans;
    }
    int succ(int v) {
        int x = root, ans = INF;
        while (x) {
            if (val[x] > v) {
                ans = val[x];
                x = ch[x][0];
            } else {
                x = ch[x][1];
            }
        }
        return ans;
    }
} tree[N << 2];
int a[N];
void build(int o, int l, int r) {
    for (int i = l; i <= r; ++i) {
        tree[o].insert(tree[o].root, a[i]);
    }
    if (l == r) return;
    int mid = l + r >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
}
void modify(int o, int l, int r, int x, int v) {
    tree[o].del(tree[o].root, a[x]);
    tree[o].insert(tree[o].root, v);
    if (l == r) return;
    int mid = l + r >> 1;
    x <= mid ? modify(o << 1, l, mid, x, v) : modify(o << 1 | 1, mid + 1, r, x, v);
}
int queryrank(int o, int l, int r, int x, int y, int v) {
    if (x <= l && y >= r) return tree[o].rank(v);
    int mid = l + r >> 1, ans = 0;
    if (x <= mid) ans += queryrank(o << 1, l, mid, x, y, v);
    if (y > mid) ans += queryrank(o << 1 | 1, mid + 1, r, x, y, v);
    return ans;
}
int querypred(int o, int l, int r, int x, int y, int v) {
    if (x <= l && y >= r) return tree[o].pred(v);
    int mid = l + r >> 1, ans = -INF;
    if (x <= mid) ans = max(ans, querypred(o << 1, l, mid, x, y, v));
    if (y > mid) ans = max(ans, querypred(o << 1 | 1, mid + 1, r, x, y, v));
    return ans;
}
int querysucc(int o, int l, int r, int x, int y, int v) {
    if (x <= l && y >= r) return tree[o].succ(v);
    int mid = l + r >> 1, ans = INF;
    if (x <= mid) ans = min(ans, querysucc(o << 1, l, mid, x, y, v));
    if (y > mid) ans = min(ans, querysucc(o << 1 | 1, mid + 1, r, x, y, v));
    return ans;
}
int main() {
#ifdef LOCAL
    freopen("..\\in", "r", stdin), freopen("..\\out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    build(1, 1, n);
    while (m--) {
        switch (int cmd, l, r, k; (cin >> cmd), cmd) {
        case 1: // 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
            cin >> l >> r >> k;
            cout << queryrank(1, 1, n, l, r, k) + 1 << '\n';
            break;
        case 2: // 若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
            cin >> l >> r >> k;
            {
                int low = 0, high = 100000000;
                while (low < high) {
                    int mid = low + high >> 1, t = queryrank(1, 1, n, l, r, mid);
                    if (t < k) {
                        low = mid + 1;
                    } else {
                        high = mid;
                    }
                }
                cout << low - 1 << '\n';
            }
            break;
        case 3: // 若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
            cin >> l >> k;
            modify(1, 1, n, l, k);
            a[l] = k;
            break;
        case 4: // 若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
            cin >> l >> r >> k;
            cout << querypred(1, 1, n, l, r, k) << '\n';
            break;
        case 5: // 若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继
            cin >> l >> r >> k;
            cout << querysucc(1, 1, n, l, r, k) << '\n';
            break;
        }
    }
    return 0;
}

Cartesian tree \text{Cartesian tree} Cartesian tree

中文叫笛卡尔树,笛卡尔树可以简单地定义,给定一个数组 A A A 长度为 n n n (下标从 0 0 0 开始),他的笛卡尔树的根是 A A A 中的最小值,设为 A min ⁡ A_{\min} Amin min ⁡ \min min 是位置的下标),左子树和右子树分别为 A 0 , A 1 , ⋯   , A min ⁡ − 1 A_0,A_1,\cdots,A_{\min-1} A0,A1,,Amin1 的笛卡尔树和 A min ⁡ + 1 , A min ⁡ + 2 , ⋯   , A n − 1 A_{\min+1},A_{\min+2},\cdots,A_{n-1} Amin+1,Amin+2,,An1 的笛卡尔树,空数组的笛卡尔树也为空树。

按照定义的话用线段树或者 ST \text{ST} ST 表都可以随便构造,但是复杂度比较高。

构造笛卡尔树可以用栈(单调栈),一直退栈直到栈为空或者栈顶元素比当前元素小,当前元素的左孩子为刚刚最后一次退栈的元素(如果栈在执行 pop \text{pop} pop 之前就是空就是空),当前元素的双亲是当前栈顶元素(如果栈现在为空则为空,用这个可以更新笛卡尔树的根),复杂度为 Θ ( n ) \Theta(n) Θ(n) Treap \text{Treap} Treap 在某种意义上也是笛卡尔树。

笛卡尔树可以在线性时间把后缀数组转换为后缀树(待补充)。

RMQ \text{RMQ} RMQ 中询问 [ A l , A r ] [A_l,A_r] [Al,Ar] 中的最小值,显然是 A l , A r A_l,A_r Al,Ar 笛卡尔树中分别对应的两个节点的最近公共祖先的值。

PS 在一些应用中不需要构造出笛卡尔树,用栈模拟就行。

题目:P5854 【模板】笛卡尔树

#include 
using namespace std;
using ll = long long;
const int N = 1e7 + 5;
int a[N];                // 原数组
int stk[N], top = 0;     // 栈
int lc[N], rc[N], fa[N]; // 树
void build(int n) {
    for (int i = 1, last; i <= n; ++i) {
        last = 0;
        while (top && a[i] < a[stk[top - 1]]) last = stk[--top];
        fa[lc[i] = last] = i;
        if (top) rc[fa[i] = stk[top - 1]] = i;
        stk[top++] = i;
    }
}
int main() {
#ifdef LOCAL
    freopen("..\\in", "r", stdin), freopen("..\\out", "w", stdout);
#endif
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    build(n);
    ll l = 0, r = 0;
    for (int i = 1; i <= n; ++i) {
        l ^= static_cast<ll>(i) * static_cast<ll>(lc[i] + 1);
        r ^= static_cast<ll>(i) * static_cast<ll>(rc[i] + 1);
    }
    cout << l << ' ' << r;
    return 0;
}

你可能感兴趣的:(【模板】Treap & Cartesian tree)