非旋treap
核心思想
主要的思想与treap是一样的。通过让二叉查找树同时满足堆(随机参数)的性质来防止深度过大。与普通treap不同的是非旋treap通过树的分裂与合并来实现这一点,而非旋转。
核心操作
Update
如果是要实现类似于 set<int> 的功能,可以不用这一部分。本文以 loj104 为例,我们需要在这里更新节点的 \(Size\) 信息。
void node::Update()
{
    Size = Count;
    if (LeftChild != NULL)
        Size += LeftChild->Size;
    if (RightChild != NULL)
        Size += RightChild->Size;
    return;
}
Split
将一个非旋treap按关键字 \(x\) 分成两个,其中一个树中关键字大小不超过 \(b\) ,另一个树中关键字严格大于 \(x\)。
std::pair<node *, node *> Split(node *Rt, int x)
{
    if (Rt == NULL)
        return std::pair<node *, node *>(NULL, NULL);
    if (x < Rt->Value)
    {
        std::pair<node *, node *> Temp = Split(Rt->LeftChild, x);
        Rt->LeftChild = Temp.second;
        Rt->Update();
        return std::pair<node *, node *>(Temp.first, Rt);
    }
    else
    {
        std::pair<node *, node *> Temp = Split(Rt->RightChild, x);
        Rt->RightChild = Temp.first;
        Rt->Update();
        return std::pair<node *, node *>(Rt, Temp.second);
    }
}
Merge
合并两棵非旋treap,其中一棵中关键字严格小于另外一棵,使得新的非旋treap同时满足二叉查找树和堆的性质。
可以递归实现,每次合并使随机的 priority 小的(或大的)在上即可。
node *Merge(node *l, node *r)
{
    if (l == NULL)
        return r;
    if (r == NULL)
        return l;
    if (l->Priority <= r->Priority)
    {
        l->RightChild = Merge(l->RightChild, r);
        l->Update();
        return l;
    }
    else
    {
        r->LeftChild = Merge(l, r->LeftChild);
        r->Update();
        return r;
    }
}
其他操作
Insert & Delete
首先查询是否需要改变节点的数量。如果不需要,直接修改 Size 即可。否则:
Insert: 将树分成 小于 \(x\) 和 大于 \(x\) 两部分,然后合并这两棵树和新节点 \(x\) 。
Delete:将树分成 小于 \(x\) 、等于 \(x\) 和大于 \(x\) 三个部分,然后删除等于 \(x\) 的部分并且合并 小于 \(x\) 的部分和 大于 \(s\) 的部分。
void Insert(int x)
{
    node *T = Find(x);
    if (T != NULL)
    {
        Update(x, 1);
        return;
    }
    std::pair<node *, node *> Temp = Split(Root, x);
    Temp.first = Merge(Temp.first, new node(x));
    Root = Merge(Temp.first, Temp.second);
    return;
}
int Delete(int x)
{
    node *T = Find(x);
    if (T == NULL)
        return 1;
    if (T->Count > 1)
    {
        Update(x, -1);
        return 0;
    }
    std::pair<node *, node *> Temp1 = Split(Root, x - 1);
    std::pair<node *, node *> Temp2 = Split(Temp1.second, x);
    delete Temp2.first;
    Root = Merge(Temp1.first, Temp2.second);
    return 0;
}
Rank & Query & Precursor & Successor
这些就和一般的二叉查找树差不多,就不赘述了。
参考程序
#include <cstdio>
#include <algorithm>
const int INF = 1e7 + 10;
struct node
{
    int Value, Priority, Size, Count;
    node *LeftChild, *RightChild;
    node()
    {
        Value = Priority = Size = Count = 0;
        LeftChild = RightChild = NULL;
        return;
    }
    node(int _Value)
    {
        Value = _Value;
        Priority = rand();
        Size = Count = 1;
        LeftChild = RightChild = NULL;
        return;
    }
    inline void Update();
};
struct noneRotateTreap
{
    node *Root;
    noneRotateTreap()
    {
        Root = NULL;
        return;
    }
    inline std::pair<node *, node *> Split(node *Rt, int x);
    inline node *Merge(node *x, node *y);
    inline node *Find(int x);
    inline void Update(int x, int State);
    inline void Insert(int x);
    inline int Delete(int x);
    inline int Rank(int x);
    inline int Query(int x);
    inline int Precursor(int x);
    inline int Successor(int x);
};
noneRotateTreap NoneRotateTreap;
int main()
{
    srand((unsigned long long)"非旋treap呀");
    int n, Opt, x;
    scanf("%d", &n);
    for (; n; --n)
    {
        scanf("%d%d", &Opt, &x);
        switch (Opt)
        {
        case 1:
            NoneRotateTreap.Insert(x);
            break;
        case 2:
            NoneRotateTreap.Delete(x);
            break;
        case 3:
            printf("%d\n", NoneRotateTreap.Rank(x));
            break;
        case 4:
            printf("%d\n", NoneRotateTreap.Query(x));
            break;
        case 5:
            printf("%d\n", NoneRotateTreap.Precursor(x));
            break;
        case 6:
            printf("%d\n", NoneRotateTreap.Successor(x));
        default:
            break;
        }
    }
    return 0;
}
inline void node::Update()
{
    Size = Count;
    if (LeftChild != NULL)
        Size += LeftChild->Size;
    if (RightChild != NULL)
        Size += RightChild->Size;
    return;
}
inline std::pair<node *, node *> noneRotateTreap::Split(node *Rt, int x)
{
    if (Rt == NULL)
        return std::pair<node *, node *>(NULL, NULL);
    if (x < Rt->Value)
    {
        std::pair<node *, node *> Temp = Split(Rt->LeftChild, x);
        Rt->LeftChild = Temp.second;
        Rt->Update();
        return std::pair<node *, node *>(Temp.first, Rt);
    }
    else
    {
        std::pair<node *, node *> Temp = Split(Rt->RightChild, x);
        Rt->RightChild = Temp.first;
        Rt->Update();
        return std::pair<node *, node *>(Rt, Temp.second);
    }
}
inline node *noneRotateTreap::Merge(node *l, node *r)
{
    if (l == NULL)
        return r;
    if (r == NULL)
        return l;
    if (l->Priority <= r->Priority)
    {
        l->RightChild = Merge(l->RightChild, r);
        l->Update();
        return l;
    }
    else
    {
        r->LeftChild = Merge(l, r->LeftChild);
        r->Update();
        return r;
    }
}
inline node *noneRotateTreap::Find(int x)
{
    node *Rt = Root;
    while (Rt)
    {
        if (Rt->Value == x)
            return Rt;
        if (x < Rt->Value)
            Rt = Rt->LeftChild;
        else
            Rt = Rt->RightChild;
    }
    return NULL;
}
inline void noneRotateTreap::Update(int x, int State)
{
    node *Rt = Root;
    while (Rt)
    {
        Rt->Size += State;
        if (Rt->Value == x)
        {
            Rt->Count += State;
            return;
        }
        if (x < Rt->Value)
            Rt = Rt->LeftChild;
        else
            Rt = Rt->RightChild;
    }
    return;
}
inline void noneRotateTreap::Insert(int x)
{
    node *T = Find(x);
    if (T != NULL)
    {
        Update(x, 1);
        return;
    }
    std::pair<node *, node *> Temp = Split(Root, x);
    Temp.first = Merge(Temp.first, new node(x));
    Root = Merge(Temp.first, Temp.second);
    return;
}
inline int noneRotateTreap::Delete(int x)
{
    node *T = Find(x);
    if (T == NULL)
        return 1;
    if (T->Count > 1)
    {
        Update(x, -1);
        return 0;
    }
    std::pair<node *, node *> Temp1 = Split(Root, x - 1);
    std::pair<node *, node *> Temp2 = Split(Temp1.second, x);
    delete Temp2.first;
    Root = Merge(Temp1.first, Temp2.second);
    return 0;
}
#define LCS (Rt->LeftChild ? Rt->LeftChild->Size : 0)
inline int noneRotateTreap::Rank(int x)
{
    node *Rt = Root;
    int Ans = 0;
    while (Rt)
    {
        if (Rt->Value == x)
            return Ans + LCS + 1;
        if (x < Rt->Value)
            Rt = Rt->LeftChild;
        else
            Ans += LCS + Rt->Count, Rt = Rt->RightChild;
    }
    return Ans;
}
inline int noneRotateTreap::Query(int x)
{
    node *Rt = Root;
    while (Rt)
    {
        if (LCS < x && x <= LCS + Rt->Count)
            return Rt->Value;
        if (x <= LCS)
            Rt = Rt->LeftChild;
        else
            x -= LCS + Rt->Count, Rt = Rt->RightChild;
    }
    return 0;
}
#undef LCS
inline int noneRotateTreap::Precursor(int x)
{
    int Ans = INF;
    node *Rt = Root;
    while (Rt)
    {
        if (Rt->Value < x)
            Ans = Rt->Value, Rt = Rt->RightChild;
        else
            Rt = Rt->LeftChild;
    }
    return Ans;
}
inline int noneRotateTreap::Successor(int x)
{
    int Ans = -INF;
    node *Rt = Root;
    while (Rt)
    {
        if (Rt->Value > x)
            Ans = Rt->Value, Rt = Rt->LeftChild;
        else
            Rt = Rt->RightChild;
    }
    return Ans;
}
非旋treap的更多相关文章
- [模板] 平衡树: Splay, 非旋Treap, 替罪羊树
		简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ... 
- 非旋 treap 结构体数组版(无指针)详解,有图有真相
		非旋 $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ ... 
- 平衡树简单教程及模板(splay, 替罪羊树, 非旋treap)
		原文链接https://www.cnblogs.com/zhouzhendong/p/Balanced-Binary-Tree.html 注意是简单教程,不是入门教程. splay 1. 旋转: 假设 ... 
- 2827: 千山鸟飞绝  非旋treap
		国际惯例的题面:看起来很不可做的样子,我们先来整理一下题意吧.就是,维护每个点曾经拥有过的最大的两个属性值,支持把点的位置移动.我们用map对每个位置进行离散化,对每个位置建立一个平衡树.为了方便分离 ... 
- 2081.09.22 Kuma(非旋treap)
		描述 有N张卡片,编号从0到n-1, 刚开始从0到n-1按顺序排好. 现有一个操作, 对于p. l,表示从第p张卡片之后的l张卡片拿到 最前面. 例如n=7的时候, 刚开始卡片序列为0 1 2 3 4 ... 
- 2018.08.27 rollcall(非旋treap)
		描述 初始有一个空集,依次插入N个数Ai.有M次询问Bj,表示询问第Bj个数加入集合后的排名为j的数是多少 输入 第一行是两个整数N,M 接下来一行有N个整数,Ai 接下来一行有M个整数Bj,保证数据 ... 
- 2018.08.06 bzoj1500: [NOI2005]维修数列(非旋treap)
		传送门 平衡树好题. 我仍然是用的fhqtreap,感觉速度还行. 维护也比线段树splay什么的写起来简单. %%%非旋treap大法好. 代码: #include<bits/stdc++.h ... 
- 2018.08.05 bzoj3223: Tyvj 1729 文艺平衡树(非旋treap)
		传送门 经典的平衡树问题,之前已经用splay写过一次了,今天我突发奇想,写了一发非旋treap的版本,发现挺好写的(虽然跑不过splay). 代码: #include<bits/stdc++. ... 
- 2018.07.24 loj#107. 维护全序集(非旋treap)
		传送门 就是普通平衡树,可以拿来练非旋treap" role="presentation" style="position: relative;"&g ... 
- 2018.07.06 BZOJ 1588: HNOI2002营业额统计(非旋treap)
		1588: [HNOI2002]营业额统计 Time Limit: 5 Sec Memory Limit: 162 MB Description 营业额统计 Tiger最近被公司升任为营业部经理,他上 ... 
随机推荐
- [Vue]vue-router嵌套路由(子路由)
			总共添加两个子路由,分别命名Collection.vue(我的收藏)和Trace.vue(我的足迹) 1.重构router/index.js的路由配置,需要使用children数组来定义子路由,具体如 ... 
- 决心学HTML 第一晚
			老师数他的审美观不行,怕教的我们的审美观也不行,所以,就不讲HTML了,但是期末又要考, 自己英语又不行,期末做项目又要用,我可能又要做组长,只能硬着头皮自学了. 所以,现在从第一页开始学!!! 这这 ... 
- PHP常见算法
			算法的概念:解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作.一个问题可以有多种算法,每种算法都不同的效率.一个算法具有的特征:有穷,确切,输入,输出,可行 ... 
- 第一章 Django之学习Django所需知识(3)
			所需编程知识 本书读者需要理解基本的面向过程和面向对象编程:流程控制(if, while 和 for),数据结构(列表,哈希表/字典),变量,类和对象. Web 开发经验,正如你所想的,也是非常有帮助 ... 
- 第一课  IP通信
			我们的专业课:<IP通信>开课了. 在第一节课,我们初步了解了关于通信的知识,涨了知识,下面我就说一下第一节课所学所感. 在学习这门课的时候,需要我们认真预习,认真听课, ... 
- keil 选项卡设置
			*1.optimization : level2. *2. 2)硬件目标设置选项卡(Target),见图6所示. 图6 1:选择硬件目标设置选项卡 2:指定用于的晶振频率 3:在应用中可以选择实时操 ... 
- chkconfig  系统服务管理
			chkconfig --list 显示当前运行的所有服务/etc/inittabnetstat -antl all n端口 t tcp协议 l listen状态chkconfig --add ... 
- Python基础之类
			一.摘要 面向对象编程 是最有效的软件编写方法之一.在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象.编写类时,你定义一大类对象都有的通用行为.基于类创建对象 时,每个对 ... 
- Netty搭建服务端的简单应用
			Netty简介 Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客 ... 
- 大数据之路week02 Collection 集合体系收尾(Set)
			1.Set集合(理解) (1)Set集合的特点 无序,唯一. (2)HashSet集合(掌握) A: 底层数据结构是哈希表(是一个元素为链表的数组) B: 哈希表底层依赖两个方法: hashCode( ... 
