[Bzoj3224][Tyvj1728] 普通平衡树(splay/无旋Treap)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224
平衡树入门题,学习学习。
splay(学习yyb巨佬)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = ;
const int INF = 1e9;
inline void read(int &n) {
register int x = , t = ;
register char ch = getchar();
while (ch != '-' && (ch<'' || ch>''))ch = getchar();
if (ch == '-') { t = -; ch = getchar(); }
while (ch >= ''&&ch <= '') { x = x * + ch - ; ch = getchar(); }
n = x * t;
}
int root, len;
struct node {
int fa;//节点的父节点
int siz;//节点的子树数量
int ch[];//节点的左(0)子节点和右(1)子节点
int val;//节点的值
int num;//节点的值的数量
}a[maxn];
inline void pushup(int x) {//更新当前节点的子树//当前子树的大小是左子树大小加上右子树大小当前当前节点个数
a[x].siz = a[a[x].ch[]].siz + a[a[x].ch[]].siz + a[x].num;
}
inline void rotate(int x) {//旋转操作//将x旋转到x的父亲的位置
int y = a[x].fa;//y是x的父节点
int z = a[y].fa;//z是x的祖父节点
int k = (a[y].ch[] == x);//求x是y的左(0)还是右(1)节点
a[z].ch[a[z].ch[] == y] = x;//求y是z的左(0)还是右(1)节点,同时将x放在该位置
a[x].fa = z;//修改x的父节点
a[y].ch[k] = a[x].ch[k ^ ];//把x的儿子给y
a[a[x].ch[k ^ ]].fa = y;//更新父节点为y
a[x].ch[k ^ ] = y;//y变为x的子节点
a[y].fa = x;//y的父亲更新为x
pushup(y), pushup(x);//更新y和x的子节点数量
}
inline void splay(int x, int goal) {//将x旋转为goal的子节点
while (a[x].fa != goal) {
int y = a[x].fa;
int z = a[y].fa;
if (z != goal)
(a[y].ch[] == x) ^ (a[z].ch[] == y) ? rotate(x) : rotate(y);
//如果x和y同为左儿子或者右儿子先旋转y
//如果x和y不同为左儿子或者右儿子先旋转x
//如果不双旋的话,旋转完成之后树的结构不会变化
rotate(x);
}
if (goal == )
root = x;
}
inline void insert(int x) {
int now = root, fa = ;//当前节点now和now的父节点fa
while (now&&a[now].val != x) {//当now存在并且没有移动到当前的值
fa = now;//父节点变为now
now = a[now].ch[x > a[now].val];//x大于当前位置则now向右,否则now向左
}
if (now)//这个值出现过
a[now].num++;//值增加
else {//这个值第一次出现,要新建一个节点来存放
now = ++len;
if (fa)//父节点不是根节点(0)
a[fa].ch[x > a[fa].val] = now;
//初始化节点
a[now].ch[] = a[now].ch[] = ;
a[now].siz = ;
a[now].num = ;
a[now].fa = fa;
a[now].val = x;
}
splay(now, );
}
inline void Find(int x) {//查找x的位置,并将其旋转到根节点
int now = root;
if (!now)return;//数为空
while (a[now].ch[x > a[now].val] && x != a[now].val)////当存在儿子并且当前位置的值不等于x
now = a[now].ch[x > a[now].val];
splay(now, );//把当前位置旋转到根节点
}
inline int Next(int x, int flag) {//查找x的前驱(0)或者后继(1)
Find(x);
int now = root;
if (a[now].val > x&&flag)return now;//如果当前节点的值大于x并且要查找的是后继
if (a[now].val < x && !flag)return now;//如果当前节点的值小于x并且要查找的是前驱
now = a[now].ch[flag];//查找后继的话在右儿子上找,前驱在左儿子上找
while (a[now].ch[flag ^ ])now = a[now].ch[flag ^ ];
return now;
}
inline void Delete(int x) {//删除一个x
int last = Next(x, );//查找x的前驱
int next = Next(x, );//查找x的后继
splay(last, ), splay(next, last);
//将前驱旋转到根节点,后继旋转到根节点下面
//很明显,此时后继是前驱的右儿子,x是后继的左儿子,并且x是叶子节点
int now = a[next].ch[];//后继的左儿子,也就是x
if (a[now].num > ) {//超过一个就删除一个
a[now].num--;
splay(now, );
}
else
a[next].ch[] = ;//直接删除 }
inline int kth(int k) {//查找第k小的数
int now = root;
if (a[now].siz < k)
return ;
while () {
int y = a[now].ch[];//y是左儿子
if (k > a[y].siz + a[now].num) {//如果排名比左儿子的大小和当前节点的数量要大
k -= a[y].siz + a[now].num;//排名减小
now = a[now].ch[];//定在右儿子上找
}
else {
if (k <= a[y].siz)
now = y;
else
return a[now].val;
}
}
}
int main() {
int n;
read(n);
root = , len = ;
insert();
insert(-);
while (n--) {
int q, x;
read(q), read(x);
if (q == )
insert(x);
else if (q == )
Delete(x);
else if (q == ) {
Find(x);
printf("%d\n", a[a[root].ch[]].siz);
}
else if (q == )
printf("%d\n", kth(x + ));
else if (q == )
printf("%d\n", a[Next(x, )].val);
else if (q == )
printf("%d\n", a[Next(x, )].val);
}
}
无旋Treap(学习fzszkl巨佬)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + ;
int Siz[maxn], ls[maxn], rs[maxn], val[maxn], pos[maxn], root;
int cnt;
int send = ;
inline int Rand() {
send ^= send << ;
send ^= send >> ;
send ^= send << ;
return send;
}
inline int New(int x) {
cnt++;
pos[cnt] = Rand();
Siz[cnt] = ;
val[cnt] = x;
return cnt;
}
inline void up(int x) {
Siz[x] = Siz[ls[x]] + Siz[rs[x]] + ;
}
void split_size(int x, int siz, int &A, int &B) {
if (x == )return (void)(A = B = );
if (siz <= Siz[ls[x]])
B = x, split_size(ls[x], siz, A, ls[x]);
else
A = x, split_size(rs[x], siz - Siz[ls[x]] - , rs[x], B);
up(x);
}
void split_val(int x, int v, int &A, int &B) {
if (x == )return (void)(A = B = );
if (v < val[x])
B = x, split_val(ls[x], v, A, ls[x]);
else
A = x, split_val(rs[x], v, rs[x], B);
up(x);
}
int Merge(int A, int B) {
if (A == || B == )return A | B;
int ans;
if (pos[A] > pos[B])ans = A, rs[A] = Merge(rs[A], B);
else ans = B, ls[B] = Merge(A, ls[B]);
up(ans);
return ans;
}
void dfs(int x) {
if (x == )
return;
printf("%d ", x);
dfs(ls[x]);
dfs(rs[x]);
}
void insert(int x) {
int A, B;
split_val(root, x - , A, B);
root = Merge(Merge(A, New(x)), B);
}
void Delete(int x) {
int A, B, C, D;
split_val(root, x - , A, B);
split_size(B, , C, D);//C就是删除的节点
root = Merge(A, D);
}
int Rank(int x) {//查x的排名
int A, B, ans;
split_val(root, x - , A, B);
ans = Siz[A] + ;
root = Merge(A, B);
return ans;
}
int getR(int x) {//查第x名是什么
int A, B, C, D, ans;
split_size(root, x - , A, B);
split_size(B, , C, D);
ans = val[C];
Merge(Merge(A, C), D);
return ans;
}
int last(int x) {
int A, B, C, D, ans;
split_val(root, x - , A, B);
split_size(A, Siz[A] - , C, D);
ans = val[D];
Merge(Merge(C, D), B);
return ans;
}
int Next(int x) {
int A, B, C, D, Ans;
split_val(root, x, A, B);
split_size(B, , C, D);
Ans = val[C];
root = Merge(Merge(A, C), D);
return Ans;
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int opt, x;
scanf("%d%d", &opt, &x);
if (opt == )
insert(x);
else if (opt == )
Delete(x);
else if (opt == )
printf("%d\n", Rank(x));
else if (opt == )
printf("%d\n", getR(x));
else if (opt == )
printf("%d\n", last(x));
else if (opt == )
printf("%d\n", Next(x));
}
return ;
}
[Bzoj3224][Tyvj1728] 普通平衡树(splay/无旋Treap)的更多相关文章
- [Bzoj3223][Tyvj1729] 文艺平衡树(splay/无旋Treap)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3223 平衡树处理区间问题的入门题目,普通平衡树那道题在维护平衡树上是以每个数的值作为维护 ...
- BZOJ - 3223 Tyvj 1729 文艺平衡树 (splay/无旋treap)
题目链接 splay: #include<bits/stdc++.h> using namespace std; typedef long long ll; ,inf=0x3f3f3f3f ...
- 【序列操作V】平衡树(无旋treap)
题目描述 维护一个队列,初始为空.依次加入 n(1≤n≤105)个数 ai(-109≤ai≤109),第 i(1≤i≤n)个数加入到当前序列第 bi(0≤bi≤当前序列长度)个数后面.输出最终队列. ...
- [模板] 平衡树: Splay, 非旋Treap, 替罪羊树
简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...
- 浅谈无旋treap(fhq_treap)
一.简介 无旋Treap(fhq_treap),是一种不用旋转的treap,其代码复杂度不高,应用范围广(能代替普通treap和splay的所有功能),是一种极其强大的平衡树. 无旋Treap是一个叫 ...
- [转载]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
转载自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182491.html 今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和t ...
- [您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我 ...
- 洛谷 - P3391 【模板】文艺平衡树(Splay) - 无旋Treap
https://www.luogu.org/problem/P3391 使用无旋Treap维护序列,注意的是按顺序插入的序列,所以Insert实际上简化成直接root和Merge合并,但是假如要在序列 ...
- Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...
随机推荐
- 二分查找法(java版)
二分查找法也称为折半查找法,在有序的序列中使用二分法可以提高程序的执行效率. 典型的二分查找法代码 public int binarySearch1(int[] arr,int target){ in ...
- wepy的第一个demo
一.在node中安装相应的模块文件,查看文档 二.案例 json部分 { "usingComponents": { "van-button": ".. ...
- open, creat - 用来 打开和创建 一个 文件或设备
SYNOPSIS 总览 #includ e <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int o ...
- JS 控制子页面刷新父页面
iframe里面的子页,用parent.location.href = parent.location.reload();如果是window.open 打开就用opener.location.relo ...
- union 横向组合
select sum(zs) zs,sum(zl) zl,sum(ts) ts,sum(lxcbw) lxcbw,sum(bz) bz,sum(sfzqt) sfzqtfrom (select cou ...
- ForkJoinPool源码简单解析
ForkJoin框架之ForkJoinTask java 阅读约 62 分钟 前言 在前面的文章"CompletableFuture和响应式编程"中提到了ForkJoinTas ...
- vue 打包上线后 所使用的css3渐变属性丢失的问题解决方案
最近在做vue项目的时候用到了css3渐变属性,本地跑项目没问题,但是打包放到服务器后发现这个属性丢失了.如下图: .join{ position:absolute; left:1rem; botto ...
- Kettle连接MySQL错误:OPTION SQL_SELECT_LIMIT=DEFAULT
由于升级了MySQL到5.6,运行ETL报错: OPTION SQL_SELECT_LIMIT=DEFAULT 上网查询原来是MySQL的驱动版本不一致,之前的驱动不支持这样的写法,于是上网下载对应的 ...
- 使用rabbitctl添加用户
使用rabbitctl添加用户 第一.添加mq用户并设置密码 root@live-mq-01:~ # rabbitmqctl add_user mq 123456 1 root@live-mq-01: ...
- 类数组对象与 arguments
类数组对象:拥有一个 length 属性和若干索引属性的对象 var array = ['name', 'age', 'sex']; var arrayLike = { 0: 'name', 1: ' ...