知识点 - 二逼平衡树(树套树)

知识点 - 二逼平衡树(树套树)

解决问题类型:

  1. 查询k在区间内的排名
  2. 查询区间内排名为k的值
  3. 修改某一位值上的数值
  4. 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
  5. 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

也就是说,我们用一颗线段树管理下标区间 [L,R][L,R] ,然后每个线段树的节点都是一颗平衡树,平衡树中存储了所有下标在 [L,R][L,R]之间的所有数的信息。这样的话,所有区间都可以被分为 O ( log ⁡ N ) ​ O(\log N)​ O(logN)个线段树的节点,区间问题便转化为了几个平衡树上的问题。

复杂度:

1、查询区间内一个数的排名:在线段树上找到区间对应的节点,然后每个节点的平衡树内查询对应数的排名并求和。时间复杂度 O ( log ⁡ 2 N ) O(\log^2N) O(log2N)

2、查询区间内排名为k的数是几:由于这项操作在线段树上不可加。所以我们考虑转换为判定一个数是不是区间内排名为k的。这个可以在 O ( log ⁡ 2 N ) O(\log^2N) O(log2N)的时间内通过操作1完成。那么我们考虑二分答案,就可以解决这个问题。时间复杂度 O ( log ⁡ 3 N ) O(\log^3N) O(log3N)

3、单点修改:我们在线段树上找到所有覆盖这个点的区间,然后在所有区间对应的平衡树中删除原数,加入新数即可。时间复杂度 O ( log ⁡ 2 N ) O(\log^2N) O(log2N)

4,5、 查询区间内一个数前驱、后继:这是平衡树上的经典问题。我们只需要对于所有区间分别查询,然后答案相应的取 max ⁡ min ⁡ \max \min maxmin 即可。时间复杂度 O ( log ⁡ 2 N ) O(\log^2N) O(log2N)

当然,由于序列中的一个数最多被插入 O(\log N) 个平衡树内,考虑到每次修改操作最多增加并删除一个节点,所以空间复杂度为 O ( N log ⁡ N ) O(N\log N) O(NlogN)。经过我们的分析,时间复杂度为 O ( N log ⁡ 3 N ) O(N\log^3N) O(Nlog3N)。当然这是一个较松的上界。

例题

【模板】普通平衡树

代码

#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <string>
#include <cstdio>
#include <vector>
#include <bitset>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;

const int MAXN = 1e5 + 100;
int n, m;
int a[MAXN];

namespace Treap
{
    struct balanced
    {
        int w;
        int sz;
        int num;
        int fix;
        int ch[2];
    };
    int tot;
    balanced tree[MAXN * 20];
    int newnode(int w)
    {
        ++tot;
        tree[tot].w = w;
        tree[tot].fix = rand();
        tree[tot].num = 1;
        tree[tot].ch[0] = tree[tot].ch[1] = 0;
        tree[tot].sz = 1;
        return tot;
    }
    void pushup(int p)
    {
        tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
    }
    void rotate(int &p, int d)
    {
        int y = tree[p].ch[d];
        tree[p].ch[d] = tree[y].ch[d ^ 1];
        tree[y].ch[d ^ 1] = p;
        pushup(p);
        pushup(y);
        p = y;
    }
    void insert(int &p, int w)
    {
        if (!p)
            p = newnode(w);
        else if (tree[p].w == w)
            ++tree[p].num;
        else 
        {
            if (tree[p].w > w)
            {
                insert(tree[p].ch[0], w);
                if (tree[tree[p].ch[0]].fix > tree[p].fix)
                    rotate(p, 0);
            }
            else
            {
                insert(tree[p].ch[1], w);
                if (tree[tree[p].ch[1]].fix > tree[p].fix)
                    rotate(p, 1);
            }
        }
        pushup(p);
    }
    void remove(int &p, int w)
    {
        if (tree[p].w > w)
            remove(tree[p].ch[0], w);
        else if (tree[p].w < w)
            remove(tree[p].ch[1], w);
        else
        {
            if (tree[p].num > 1)
                --tree[p].num;
            else
            {
                if (!tree[p].ch[0] && !tree[p].ch[1])
                    p = 0;
                else if (!tree[p].ch[0])
                {
                    rotate(p, 1);
                    remove(tree[p].ch[0], w);
                }
                else if (!tree[p].ch[1])
                {
                    rotate(p, 0);
                    remove(tree[p].ch[1], w);
                }
                else
                {
                    if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix)
                    {
                        rotate(p, 0);
                        remove(tree[p].ch[1], w);
                    }
                    else
                    {
                        rotate(p, 1);
                        remove(tree[p].ch[0], w);
                    }
                }
            }
        }
        if (p)
            pushup(p);
    }
    int queryrank(int p, int k) // return the highest rank of value 'k'
    {
        if (!p)
            return 0;
        if (tree[p].w > k)
            return queryrank(tree[p].ch[0], k);
        else if (tree[p].w == k)
            return tree[tree[p].ch[0]].sz;
        else
            return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
    }
    int querynum(int p, int k) // return the value of kth rank node
    {
        if (tree[tree[p].ch[0]].sz + 1 == k)
            return tree[p].w;
        else if (tree[tree[p].ch[0]].sz + 1 < k)
            return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
        else    
            return querynum(tree[p].ch[0], k);
    }
    int querypre(int p, int k) // return the prefix of value k
    {
        if (!p) 
            return -2147483647;
        if (tree[p].w >= k)
            return querypre(tree[p].ch[0], k);
        else
            return max(tree[p].w, querypre(tree[p].ch[1], k));
    }
    int querysuf(int p, int k) // return the suffix of value k
    {
        if (!p)
            return 2147483647;
        if (tree[p].w <= k)
            return querysuf(tree[p].ch[1], k);
        else
            return min(tree[p].w, querysuf(tree[p].ch[0], k));
    }
    void listall(int p)
    {
        if (tree[p].ch[0])
            listall(tree[p].ch[0]);
        cerr << tree[p].w << ",sz=" << tree[p].num << "   ";
        if (tree[p].ch[1])
            listall(tree[p].ch[1]);
    }
}
using Treap::listall;

namespace SEG
{
    struct segment
    {
        int l;
        int r;
        int root;
    };
    segment tree[MAXN * 8];
    void build(int p, int l, int r)
    {
        tree[p].l = l;
        tree[p].r = r;
        for (int i = l; i < r + 1; ++i)
            Treap::insert(tree[p].root, a[i]);
        if (l != r)
        {
            int mid = (l + r) / 2;
            build(p * 2, l, mid);
            build(p * 2 + 1, mid + 1, r);
        }
    }
    void modify(int p, int x, int y)
    {
        Treap::remove(tree[p].root, a[x]);
        Treap::insert(tree[p].root, y);
        if (tree[p].l == tree[p].r)
            return;
        int mid = (tree[p].l + tree[p].r) / 2;
        if (x > mid)
            modify(p * 2 + 1, x, y);
        else
            modify(p * 2, x, y);
    }
    int queryrank(int p, int l, int r, int k) // query the highest rank of value 'k'
    {
        if (tree[p].l > r || tree[p].r < l)
            return 0;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::queryrank(tree[p].root, k);
        else
            return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
    }
    int querynum(int u, int v, int k) // query the value of kth num
    {
        int l = 0, r = 1e8;
        while (l < r)
        {
            int mid = (l + r + 1) / 2;
            if (queryrank(1, u, v, mid) < k)
                l = mid;
            else
                r = mid - 1;
        }
        return r;
    }
    int querypre(int p, int l, int r, int k)
    {
        if (tree[p].l > r || tree[p].r < l)
            return -2147483647;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::querypre(tree[p].root, k);
        else
            return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
    }
    int querysuf(int p, int l, int r, int k)
    {
        if (tree[p].l > r || tree[p].r < l)
            return 2147483647;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::querysuf(tree[p].root, k);
        else
            return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
    }
}

int read()
{
    char ch = getchar();
    int x = 0, flag = 1;
    while (ch != '-' && (ch < '0' || ch > '9'))
        ch = getchar();
    if (ch == '-')
    {
        ch = getchar();
        flag = -1;
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * flag;
}

int main()
{
    n = read();
    m = read();
    for (int i = 1; i < n + 1; ++i)
        a[i] = read();
    SEG::build(1, 1, n);
    for (int i = 0; i < m; ++i)
    {
        int opt = read();
        if (opt == 3)
        {
            int x = read(), y = read();
            SEG::modify(1, x, y);
            a[x] = y;
        }
        else
        {
            int l = read(), r = read(), k = read();
            if (opt == 1)
                printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
            else if (opt == 2)
                printf("%d\n", SEG::querynum(l, r, k));
            else if (opt == 4)
                printf("%d\n", SEG::querypre(1, l, r, k));
            else
                printf("%d\n", SEG::querysuf(1, l, r, k));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值