核心思想

主要的思想与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

这些就和一般的二叉查找树差不多,就不赘述了。

参考程序

loj 104

#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的更多相关文章

  1. [模板] 平衡树: Splay, 非旋Treap, 替罪羊树

    简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...

  2. 非旋 treap 结构体数组版(无指针)详解,有图有真相

    非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ  ...

  3. 平衡树简单教程及模板(splay, 替罪羊树, 非旋treap)

    原文链接https://www.cnblogs.com/zhouzhendong/p/Balanced-Binary-Tree.html 注意是简单教程,不是入门教程. splay 1. 旋转: 假设 ...

  4. 2827: 千山鸟飞绝 非旋treap

    国际惯例的题面:看起来很不可做的样子,我们先来整理一下题意吧.就是,维护每个点曾经拥有过的最大的两个属性值,支持把点的位置移动.我们用map对每个位置进行离散化,对每个位置建立一个平衡树.为了方便分离 ...

  5. 2081.09.22 Kuma(非旋treap)

    描述 有N张卡片,编号从0到n-1, 刚开始从0到n-1按顺序排好. 现有一个操作, 对于p. l,表示从第p张卡片之后的l张卡片拿到 最前面. 例如n=7的时候, 刚开始卡片序列为0 1 2 3 4 ...

  6. 2018.08.27 rollcall(非旋treap)

    描述 初始有一个空集,依次插入N个数Ai.有M次询问Bj,表示询问第Bj个数加入集合后的排名为j的数是多少 输入 第一行是两个整数N,M 接下来一行有N个整数,Ai 接下来一行有M个整数Bj,保证数据 ...

  7. 2018.08.06 bzoj1500: [NOI2005]维修数列(非旋treap)

    传送门 平衡树好题. 我仍然是用的fhqtreap,感觉速度还行. 维护也比线段树splay什么的写起来简单. %%%非旋treap大法好. 代码: #include<bits/stdc++.h ...

  8. 2018.08.05 bzoj3223: Tyvj 1729 文艺平衡树(非旋treap)

    传送门 经典的平衡树问题,之前已经用splay写过一次了,今天我突发奇想,写了一发非旋treap的版本,发现挺好写的(虽然跑不过splay). 代码: #include<bits/stdc++. ...

  9. 2018.07.24 loj#107. 维护全序集(非旋treap)

    传送门 就是普通平衡树,可以拿来练非旋treap" role="presentation" style="position: relative;"&g ...

  10. 2018.07.06 BZOJ 1588: HNOI2002营业额统计(非旋treap)

    1588: [HNOI2002]营业额统计 Time Limit: 5 Sec Memory Limit: 162 MB Description 营业额统计 Tiger最近被公司升任为营业部经理,他上 ...

随机推荐

  1. 【SQL Server DBA】日常巡检语句3:特定监控(阻塞、top语句、索引、作业)

    原文:[SQL Server DBA]日常巡检语句3:特定监控(阻塞.top语句.索引.作业) 1.查询阻塞信息.锁定了哪些资源 --1.查看阻塞信息 select spid,loginame,wai ...

  2. JQuery攻略读书笔记---第2章 数组

      2 数组2.8 创建对象数组循环数组2.9 数组排序 2 数组 2.8 创建对象数组 //数组化对象 var student =[ { "role":101, "na ...

  3. Java 面向对象_继承

    继承 在继承的关系中,子类就是一个父类,也就是说,子类可以被当做父类看待,例如:父类是员工,子类是程序员,那么程序员就是一个员工,代码示例: // 员工类 public class Employee{ ...

  4. nodejs入门API之url模块+querystring模块

    关于URL的一些基础内容 URL模块的API解析 URL的参数URLSearchParams类 querystring模块 一.关于URL的一些基础内容 1.1 定义: 在WWW上,每一信息资源都有统 ...

  5. Linux Centos7配置ftp服务器

    一.安装 1.安装 yum install  -y vsftpd 2.设置开机启动 systemctl enable vsftpd.service 3.启动 systemctl start vsftp ...

  6. linux设置自动同步服务器时间

    最近遇到一个问题,由于两台服务器时间的问题,经常导致用户登录由于时间差问题而报错,再三百度,最后整理了一下修改linux定时同步的操作(本方法适用于有自己时间服务器,没有的只限于借鉴) 首先确认,我们 ...

  7. Apache Maven setting.xml

    <?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Soft ...

  8. java中的io流总结(一)

    知识点:基于抽象基类字节流(InputStream和OutputStream).字符流(Reader和Writer)的特性,处理纯文本文件,优先考虑使用字符流BufferedReader/Buffer ...

  9. zznu-oj-2134- 发红包!!!-【多项式加法,模拟题目】

    2134: 发红包!!! 题目描述 给你两个最简多项式,请输出两个多项式相加后的结果.给定的多项式的格式为ax^num1+bx^num2+...其中x前面的a,b代表系数num代表指数(次方数),输入 ...

  10. 记录一下关于DQN的想法

    下载了几份代码,就两份没有报错通过了 DQN玩FlappyBird https://github.com/yenchenlin/DeepLearningFlappyBird DQN玩Cartpole ...