day 2

平衡树 二叉查找树 我们在OI中经常需要这样一种数据结构:
一个集合支持快速插入、删除一个数字。
支持快速查找一个数字在所有已插入数字中的排名。
支持删除大小在某一个区间内的数字。
动态维护一个数列。可以在数列的任何位置插入删除,求区间和,Min,Max,进行区间翻转。
Treap

SPLAY

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;

const int INF = 1e9;
const int MAX_Q = 1e5 + 10;

struct splay_node 
{
    int value, size, cnt;
    splay_node *pre, *ch[2];
    void update() 
    {
        size = ch[0]->size + ch[1]->size + cnt;
    }
    int get_wh()
    {
        return pre->ch[0] == this ? 0 : 1;
    }
    void set_ch(int wh, splay_node *child);
} pool[MAX_Q], *root, *null;

void splay_node::set_ch(int wh, splay_node *child)
{
    ch[wh] = child;
    if (child != null) child->pre = this;
    update();
}

int top = 0;

inline splay_node *get_new(int value)
{
    splay_node *now = pool + ++top;
    now->value = value;
    now->size = 1;
    now->cnt = 1;
    now->pre = now->ch[0] = now->ch[1] = null;
    return now;
}

inline void rotate(splay_node *&now)
{
    splay_node *old_father = now->pre, 
        *grand = now->pre->pre;
    int wh = now->get_wh();
    old_father->set_ch(wh, now->ch[wh ^ 1]);
    now->set_ch(wh ^ 1, old_father);
    now->pre = grand;
    if (grand != null)
        grand->ch[grand->ch[0] == old_father ? 0 : 1] = now;
}

inline void splay(splay_node *now, splay_node *tar)
{
    for (; now->pre != tar; rotate(now))
        if (now->pre->pre != tar)
            now->get_wh() == now->pre->get_wh() ? 
                rotate(now->pre) : rotate(now);
    if (tar == null) root = now;
}

void insert(int value)
{
    splay_node *last = null, *now = root;
    splay_node *newone = get_new(value);

    while (now != null)
    {
        last = now;
        if (newone->value == now->value)
        {
            now->cnt++; now->size++;
            splay(now, null);
            return;
        }
        if (newone->value < now->value)
            now = now->ch[0];
        else
            now = now->ch[1];
    }

    if (last == null)
        root = newone;
    else
    {
        if (newone->value < last->value)
            last->set_ch(0, newone);
        else
            last->set_ch(1, newone);
        splay(newone, null);
    }
}

inline splay_node *find(int value)
{
    splay_node *now = root;
    while (now != null)
    {
        if (now->value == value)
            break;
        if (value < now->value)
            now = now->ch[0];
        else
            now = now->ch[1];
    }
    if (now != null) splay(now, null);
    return now;
}

inline int pre(int value)
{
    int ans = -INF;
    splay_node *now = root;
    while (now != null)
    {
        if (now->value < value)
        {
            ans = max(ans, now->value);
            now = now->ch[1];
        } else
            now = now->ch[0];
    }
    return ans;
}

inline int nxt(int value)
{
    int ans = INF;
    splay_node *now = root;
    while (now != null)
    {
        if (now->value > value)
        {
            ans = min(ans, now->value);
            now = now->ch[0];
        } else
            now = now->ch[1];
    }
    return ans;
}

void del(int value)
{
    splay_node *now = find(value);
    if (now == null) return;
    if (now->cnt > 1)
    {
        now->cnt--;
        now->size--;
        return;
    }

    if (now->ch[0] == null && now->ch[1] == null)
        root = null;
    else if (now->ch[1] == null)
    {
        now->ch[0]->pre = null;
        root = now->ch[0];
    }
    else if (now->ch[0] == null)
    {
        now->ch[1]->pre = null;
        root = now->ch[1];
    }
    else
    {
        splay_node *_ = now->ch[0];
        while (_->ch[1] != null) _ = _->ch[1];
        splay(_, now);
        _->set_ch(1, now->ch[1]);
        _->pre = null;
        root = _;
    }
}

inline int get_rank(int value)
{
    splay_node *now = root;
    int left_size = 0;
    while (now != null)
    {
        if (value == now->value)
        {
            int ans = left_size + now->ch[0]->size + 1;
            splay(now, null);
            return ans;
        }
        if (value < now->value)
            now = now->ch[0];
        else
            left_size += now->ch[0]->size + now->cnt, now = now->ch[1];
    }
    return -1;
}

inline int kth(int k)
{
    splay_node *now = root;
    int left_size = 0;
    while (now != null)
    {
        int _ = left_size + now->ch[0]->size;
        if (_ + 1 <= k && k <= _ + now->cnt)
        {
            splay(now, null);
            return now->value;
        }
        if (k <= _) now = now->ch[0];
        else left_size = _ + now->cnt, now = now->ch[1];
    }
    return -1;
}

inline int get_num()
{
    int num = 0;
    char c;
    bool flag = false;
    while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
    if (c == '-') flag = true;
    else num = c - '0';
    while (isdigit(c = getchar()))
        num = num * 10 + c - '0';
    return (flag ? -1 : 1) * num;
}

int main()
{
    null = pool;
    null->value = 0;
    null->size = 0;
    null->cnt = 0;
    null->pre = null->ch[0] = null->ch[1] = null;
    root = null;

    int q = get_num();
    while (q--)
    {
        int order = get_num();
        int _ = get_num();
        switch(order)
        {
            case 1:
                insert(_);
                break;
            case 2:
                del(_);
                break;
            case 3:
                printf("%d\n", get_rank(_));
                break;
            case 4:
                printf("%d\n", kth(_));
                break;
            case 5:
                printf("%d\n", pre(_));
                break;
            case 6:
                printf("%d\n", nxt(_));
                break;
        }
    }
}

Splay相比Treap的优势是能够更好的实现区间操作。
在进行区间操作之前我们通常要把我们要进行操作的区间弄到一棵子树上。

假如我们用Splay维护一个序列(也就是我们所谓的节点权值实际上是数列的下表,而数列里面数字本身的权值再开一个附加变量存储。
我们要对区间[L,R]进行操作,我们首先获得L-1节点和R+1节点。我们把L-1节点splay到根,再把R+1节点splay到根的右儿子位置。那么现在根的右儿子的左儿子这整棵子树就是我们要的区间。
因为我们在splay的过程中,一直会进行update,所以每个节点维护的附加信息(比如size,我们还可以加入sum,min,max等等)都一直是正确的。访问根节点右儿子的左儿子的信息,就可以直接获得区间信息。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值