————————————————
版权声明:本文为CSDN博主「ModestCoder_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ModestCoder_/article/details/90139481

清空一个节点
确定一个节点是父亲的左儿子还是右儿子
更新一个节点
把一个点连到另一点下面
上旋
splay
插入一个点
查询一个数的排名
查询排名为k的数
前驱、后继
删除一个点

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int sz, rt, f[maxn], key[maxn], size[maxn], recy[maxn], son[maxn][]; inline int read(){
int s = , w = ;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -;
for (; isdigit(c); c = getchar()) s = (s << ) + (s << ) + (c ^ );
return s * w;
} void clear(int x){
f[x] = key[x] = size[x] = recy[x] = son[x][] = son[x][] = ;
//5个数组全部清零
} int get(int x){
return son[f[x]][] == x;
//如果自己是父亲的右儿子,返回1;否则返回0(0为左儿子,1为右儿子)
} void update(int x){
if (x){//如果这个点存在
size[x] = recy[x];//自己重复的次数先累计
if (son[x][]) size[x] += size[son[x][]];
if (son[x][]) size[x] += size[son[x][]];
//如果存在儿子,把儿子的size累积到自己
//然后发现一个问题,更新自己的size时,必须保证儿子的size是正确的
//所以后面的操作,当牵扯到儿子和父亲时,应该先更新新儿子,后更新新父亲
}
} void connect(int x, int y, int z){//x连到y下面,关系为z
if (x) f[x] = y;//存在x,则x的父亲为y
if (y) son[y][z] = x;//存在y,y的z关系儿子为x
} void rotate(int x){//上旋x
int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);//确定x,fa的关系
connect(son[x][m ^ ], fa, m);//把要转的儿子转到父亲下,关系为m
connect(fa, x, m ^ );//把父亲转到自己下面,关系为m^1
connect(x, ffa, n);//把自己转到父亲的父亲下,关系为n
update(fa), update(x);//先更新fa,再更新自己,可以自己想想为什么是这个顺序
} void splay(int x){
for (int fa; fa = f[x]; rotate(x))//每次总是旋转自己
if (f[fa]) rotate(get(x) == get(fa) ? fa : x);//如果有爷爷(父亲的父亲),看父亲与父亲的父亲的关系决定转哪个
rt = x;//别忘了,把根赋为当前点
} void insert(int x){
if (!rt){//树中没有一个节点
rt = ++sz;
key[rt] = x;
size[rt] = recy[rt] = ;
son[rt][] = son[rt][] = ;//赋初值
return;
}
int now = rt, fa = ;
while (){
if (key[now] == x){//树中已有此点,重复+1
++recy[now];
update(now); update(fa);
splay(now);//splay一下,保证平衡
return;
}
fa = now, now = son[now][x > key[now]];//满足二叉查找树的性质,往下跑
if (!now){
++sz;
key[sz] = x;
size[sz] = recy[sz] = ;//赋初值
f[sz] = fa;//父亲是fa
son[fa][x > key[fa]] = sz;//更新父亲的新儿子
update(fa);//更新父亲的size
splay(sz);//splay一下,保证平衡
return;
}
}
} int find(int x){//查排名
int now = rt, ans = ;
while (){
if (x < key[now]){
now = son[now][]; continue;//在左子树中
}
ans += size[son[now][]];//排名加上左子树节点个数
if (x == key[now]){ splay(now); return ans + ; }//值等于当前点,splay一下,保证平衡,排名+1为当前排名
ans += recy[now];//排名加上当前节点的数的个数
now = son[now][];//在右子树中
}
} int kth(int x){//查找排名为x的数
int now = rt;
while (){
if (son[now][] && x <= size[son[now][]]){//在左子树中
now = son[now][]; continue;
}
if (son[now][]) x -= size[son[now][]];//存在左儿子,排名减去左子树节点数
if (x <= recy[now]){ splay(now); return key[now]; }//说明就是当前点,splay一下,保证平衡,退出
x -= recy[now];//排名减去当前节点数的个数
now = son[now][];//在右子树中
}
} int pre(){//前驱为左子树中最大的那个
int now = son[rt][];
while (son[now][]) now = son[now][];
return now;
} int nxt(){//后继为右子树中最小的那个
int now = son[rt][];
while (son[now][]) now = son[now][];
return now;
} void del(int x){
int no_use = find(x);//find主要是把当前数的对应点找到,然后旋到根,返回值的排名在这里没用
if (recy[rt] > ){//情况1:有重复,重复-1,更新,退出
--recy[rt];
update(rt);
return;
}
//接下来都是没有重复的情况
if (!son[rt][] && !son[rt][]){//情况2:没有儿子,直接清空
clear(rt);
rt = ;
return;
}
if (!son[rt][]){//情况3:没有左儿子,只有右儿子,右儿子变成根,清除自己
int tmp = rt;
f[rt = son[rt][]] = ;
clear(tmp);
return;
}
if (!son[rt][]){//情况4:没有右儿子,只有左儿子,左儿子变成根,清除自己
int tmp = rt;
f[rt = son[rt][]] = ;
clear(tmp);
return;
}
//情况5:两个儿子都有,这是需要一个很简便的做法
//把前驱splay到根,保持左子树其他节点不用动
//原根右儿子变成前驱的右儿子
//原根功成身退,清除掉
//最后对前驱的size进行更新
int tmp = rt, left = pre();
splay(left);
connect(son[tmp][], rt, );
clear(tmp);
update(rt);
} int main(){
int M = read();
while (M--){
int opt = read(), x = read();
if (opt == ) insert(x);
if (opt == ) del(x);
if (opt == ) printf("%d\n", find(x));
if (opt == ) printf("%d\n", kth(x));
if (opt == ){
insert(x); printf("%d\n", key[pre()]); del(x);
}
if (opt == ){
insert(x); printf("%d\n", key[nxt()]); del(x);
}
}
return ;
}

luogu P3369 【模板】普通平衡树的更多相关文章

  1. [luogu P3369]【模板】普通平衡树(Treap/SBT)

    [luogu P3369][模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删 ...

  2. 数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline _ ...

  3. 替罪羊树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 闲的没事,把各种平衡树都写写 比较比较... 下面是替罪羊树 #include <cstdio> #inc ...

  4. 红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) 喜闻乐见拿到了luo ...

  5. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

  6. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  7. Luogu P2742 模板-二维凸包

    Luogu P2742 模板-二维凸包 之前写的实在是太蠢了.于是重新写了一个. 用 \(Graham\) 算法求凸包. 注意两个向量 \(a\times b>0\) 的意义是 \(b\) 在 ...

  8. luoguP3391[模板]文艺平衡树(Splay) 题解

    链接一下题目:luoguP3391[模板]文艺平衡树(Splay) 平衡树解析 这里的Splay维护的显然不再是权值排序 现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶...) 那么,继续 ...

  9. luoguP3369[模板]普通平衡树(Treap/SBT) 题解

    链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) 平衡树解析 #include<iostream> #include<cstdlib> #includ ...

  10. 【luogu P3369 普通平衡树(Treap/SBT)】 模板 Splay

    题目链接:https://www.luogu.org/problemnew/show/P3369 #include <cstdio> #include <algorithm> ...

随机推荐

  1. 根据范围爬TMS规则瓦片

    因为需要简单写了一个下载地图的爬虫,代码如下: #coding=utf-8 import urllib.request import os import socket import zlib impo ...

  2. Shell之作业控制

    命令 含义 jobs 列出所有正在运行的作业 ^Z(Ctrl+z) 暂停作业 bg 启动被暂停的作业 fg 将后台作业调到前台 kill 向指定作业发送kill信号 nohup 忽略所有发送给子命令的 ...

  3. 理解Java虚拟机中的栈、堆、堆栈

    JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) 栈区: 每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不 ...

  4. 如何使用jmeter做一个功能的性能测试

    一.为什么又再次写类似的文章? 在博客园和公号写文章,已经快两年了,所以自然在公号和博客园都能联系到我的. 也就是几天前,有个人加我微信,对于总有人加我好友,我已经觉得不奇怪了,为什么呢? 加我好友的 ...

  5. Windows玩转Kubernetes系列3-Centos安装K8S

    以往文章参考: Windows玩转Kubernetes系列1-VirtualBox安装Centos Windows玩转Kubernetes系列2-Centos安装Docker 安装K8S yum in ...

  6. P2571 [SCOI2010]传送带——hyl天梦

    P2571 [SCOI2010]传送带题解----天梦 如写的不好,请多见谅. 对于这道题,我首先想说,确实困惑了我好久,看网上的各种题解,却都不尽人意,思路早已明白,却不会操作.最后想想,还是觉得自 ...

  7. StackExchange.Redis 之 hash 类型示例

    StackExchange.Redis 的组件封装示例网上有很多,自行百度搜索即可. 这里只演示如何使用Hash类型操作数据: // 在 hash 中存入或修改一个值 并设置order_hashkey ...

  8. java9循环结构进阶

    public class jh_01_循环嵌套 { public static void main(String[] args) { // for(int i = 1;i<= 5;i++) { ...

  9. Java并发读书笔记:Lock与ReentrantLock

    Lock位于java.util.concurrent.locks包下,是一种线程同步机制,就像synchronized块一样.但是,Lock比synchronized块更灵活.更复杂. 话不多说,我们 ...

  10. ajax jsonp跨域 【转】

    跨域的基本原理:    JSONP跨域GET请求是一个常用的解决方案,    JSONP的最基本的原理是:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的 ...