斜堆,非旋转treap,替罪羊树
一、斜堆
斜堆是一种可以合并的堆
节点信息:
struct Node {
int v;
Node *ch[];
};
主要利用merge函数
Node *merge(Node *x, Node *y) {
if(!x) return y;
if(!y) return x;
if(x->v < y->v) swap(x, y);
x->ch[] = merge(x->ch[], y);
return swap(x->ch[], x->ch[]), x;
}
左偏树需要维护一个额外的信息,而斜堆每次强制swap(ch[0], ch[1]),以达到均摊$O(\log{n})$的效果
利用merge函数可以很容易地实现插入和删除
void ins(Node*& o, int x) {
o = merge(o, new Node(x));
}
void del(Node*& o) {
o = merge(o->ch[], o->ch[]);
}
另外地,堆相对与平衡树来说无法删除一个元素,但是如果能够定位到这个指针就可以删除这个元素了,方法是存储父亲(merge里要维护一下fa)
删除一个元素时可以这样
void del(Node*& o) {
Node* p = o->fa;
Node* r = merge(o->ch[], o->ch[]);
r->fa = p;
p->ch[p->ch[] == o] = r;
}
当然,和其他树形结构一样,堆上也是可以维护tag的
加入maintain()函数和down()函数后就可以轻松实现这些功能,方法和其他树形结构类似
需要注意的是删除时要像自底向上的splay一样把从这个点到根的路径上的点都down()一遍,再调用del()函数
另外,由于堆高度是均摊$O(\log{n})$的,所以在处理集合信息时不需要额外的并查集,只需要每次暴力往上找到根来比较就能够做到$O(\log{n})$的复杂度了。
!!!
!!!我现在收回 高度并不是均摊$O(\log{n})$高度是可以很大的 是会被卡的!!!!
举一例带标记的题
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string> using namespace std; void setIO(const string& a) {
freopen((a+".in").c_str(), "r", stdin);
freopen((a+".out").c_str(), "w", stdout);
} typedef long long ll;
const int N = + ; struct Node* pis;
struct Node {
ll v, a, m;
int id, fk, tag;
Node* ch[]; void add_tag(ll add, ll mul, int ft) {
if(this) {
fk += ft;
tag += ft;
(v *= mul) += add;
m *= mul;
(a *= mul) += add;
}
} void down() {
ch[]->add_tag(a, m, tag);
ch[]->add_tag(a, m, tag);
tag = a = , m = ;
} void *operator new(size_t) {
return pis++;
} Node(ll v = , int id = ) : v(v), id(id) {
ch[] = ch[] = ;
a = ;
m = ;
fk = tag = ;
}
}pool[N], *root[N]; Node *merge(Node *l, Node *r) {
if(!l || !r) return l ? l : r;
if(l->v > r->v) swap(l, r);
l->down();
l->ch[] = merge(l->ch[], r);
return swap(l->ch[], l->ch[]), l;
} ll h[N], f[N], a[N], v[N];
int ansn[N], ansm[N]; template<typename Q> void gt(Q& x) {
static char c;
static bool f;
for(f = ; c = getchar(), !isdigit(c); ) if(c == '-') f = ;
for(x = ; isdigit(c); c = getchar()) x = x * + c - '';
if(f) x = -x;
} void ddd(Node* o) {
if(!o) return;
o->down();
ddd(o->ch[]);
ddd(o->ch[]);
} int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif pis = pool;
int n, m;
gt(n), gt(m);
for(int i = ; i <= n; i++) {
gt(h[i]);
}
for(int i = ; i <= n; i++) {
gt(f[i]), gt(a[i]), gt(v[i]);
}
for(int i = ; i <= m; i++) {
ll s, c;
gt(s), gt(c);
root[c] = merge(root[c], new Node(s, i));
} for(int i = n; i >= ; i--) {
Node*& r = root[i];
while(r && r->v < h[i]) {
ansn[i]++;
r->down();
r = merge(r->ch[], r->ch[]);
}
ll add = , mul = ;
if(a[i] == ) add += v[i];
else mul *= v[i];
r->add_tag(add, mul, );
root[f[i]] = merge(root[f[i]], r);
} for(int i = ; i <= n; i++) {
printf("%d\n", ansn[i]);
}
ddd(root[]);
for(int i = ; i < m; i++) {
ansm[pool[i].id] = pool[i].fk;
}
for(int i = ; i <= m; i++) {
printf("%d\n", ansm[i]);
} return ;
}
bzoj4003
二、不旋转的treap
参见 http://memphis.is-programmer.com/posts/46317.html
这种treap的实现以分裂(split)和合并(merge)操作为主体,可以解决一些区间问题,比如区间反转。
merge()的过程和斜堆的merge()很像。
其中笛卡尔建树据说是很有用的东西
附tyvj1729文艺平衡树代码,稍作修改就可以实现可持久化
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string> using namespace std; void setIO(const string& a) {
freopen((a+".in").c_str(), "r", stdin);
freopen((a+".out").c_str(), "w", stdout);
} const int N = + ; #define size(x) ((x) ? (x)->sz : 0) struct Node *pis; struct Node {
int fix, v, sz;
bool flip;
Node *l, *r;
Node(int v = ) : v(v) {
fix = rand();
l = r = ;
sz = ;
flip = ;
}
void maintain() {
sz = size(l) + size(r) + ;
}
void down() {
if(flip) {
swap(l, r);
if(l) l->flip ^= ;
if(r) r->flip ^= ;
flip = ;
}
} void* operator new(size_t) {
return pis++;
}
}pool[N], *root;
Node *merge(Node *x, Node *y) {
if(!x) return y;
if(!y) return x;
if(x->fix < y->fix) {
x->down();
x->r = merge(x->r, y);
return x->maintain(), x;
}else {
y->down();
y->l = merge(x, y->l);
return y->maintain(), y;
}
} typedef pair<Node*, Node*> Root; Root split(Node *x, int k) {
if(!x) return Root(, );
Root y; x->down();
if(size(x->l) >= k) {
y = split(x->l, k);
x->l = y.second;
y.second = x;
}else {
y = split(x->r, k - size(x->l) - );
x->r = y.first;
y.first = x;
}
return x->maintain(), y;
} Node *build(int n) {
static Node *stk[N], *x, *last;
int p = ;
for(int i = ; i <= n; i++) {
x = new Node(i);
last = ;
while(p && stk[p]->fix > x->fix) {
stk[p]->maintain();
last = stk[p--];
}
if(p) stk[p]->r = x;
x->l = last;
stk[++p] = x;
}
while(p) stk[p--]->maintain();
return stk[];
} int n; void print(Node *o) {
if(!o) return;
o->down();
print(o->l);
if( < o->v && o->v <= n) printf("%d ", o->v);
print(o->r);
} int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif pis = pool;
int m;
scanf("%d%d", &n, &m);
root = build(n + );
while(m--) {
int l, r;
scanf("%d%d", &l, &r);
Root x = split(root, l);
Root y = split(x.second, r - l + );
y.first->flip ^= ;
root = merge(merge(x.first, y.first), y.second);
}
print(root); return ;
}
三、替罪羊树
说到不用旋转的平衡树,自然还有替罪羊树
之前好像有一种$O(n\sqrt{n})$的朝鲜树在树高为$O(\sqrt{n})$时重建整棵树
而替罪羊树在是在插入时检查如果某一个子树的左子树或右子树的大小超过该子树的$55%~80%$的时候重建这颗子树
然后就是$O(n\log{n})$的复杂度了...
值得一提的是删除
如果作为树套树的外层是很好处理的
但是如果想实现一棵普通bst的功能,却要复杂一些了。
一种方法是删除时打上删除标记,然后不管(sz信息要变成0),等到下次重建这个点的时候把这个点扔掉
还有一种没有复杂度保证但是很好写的,就是重建改点的左右子树。
附暴力重建子树的代码
tyvj1728普通平衡树
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string> using namespace std; void setIO(const string& a) {
freopen((a+".in").c_str(), "r", stdin);
freopen((a+".out").c_str(), "w", stdout);
} #define szof(x) ((x) ? (x)->sz : 0) const int N = + ; struct Node *pis;
struct Node {
int v, sz;
Node *ch[]; Node(int v = ) : v(v) {
sz = ;
ch[] = ch[] = ;
} void maintain() {
sz = szof(ch[]) + szof(ch[]) + ;
} int cmp(int x) const {
if(x == v) return -;
return x < v ? : ;
} void *operator new(size_t) {
return pis++;
}
}pool[N], *cache[N], *root;
int tot = ; void print(Node *o) {
if(!o) return;
print(o->ch[]);
cache[++tot] = o;
print(o->ch[]);
} void rebuild(Node*& o, int l, int r) {
if(l > r) return o = , void();
int mid = (l + r) >> ;
o = cache[mid];
rebuild(o->ch[], l, mid - );
rebuild(o->ch[], mid + , r);
o->maintain();
} void insert(Node*& o, int x) {
if(!o) o = new Node(x);
else {
int d = o->cmp(x);
if(d == -) d = ;
insert(o->ch[d], x);
}
o->maintain();
} void scape(Node*& o, int x) {
int d = o->cmp(x);
if(d == -) return;
if(o->ch[d]->sz > o->sz * 0.75) {
tot = ;
print(o);
rebuild(o, , tot);
}else scape(o->ch[d], x);
} void insert(int x) {
insert(root, x);
scape(root, x);
} void remove(Node*& o, int x) {
int d = o->cmp(x);
if(d == -) {
if(!o->ch[] && !o->ch[]) o = ;
else {
tot = ;
print(o->ch[]);
print(o->ch[]);
rebuild(o, , tot);
}
}else remove(o->ch[d], x);
if(o) o->maintain();
} int kth(Node *o, int k) {
while(o) {
int s = szof(o->ch[]) + ;
if(s == k) return o->v;
if(s < k) k -= s, o = o->ch[];
else o = o->ch[];
}
return -;
} int rank(Node *o, int x) {
int res = ;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == ) res += szof(o->ch[]) + ;
if(d == -) d = ;
}
return res + ;
} int pre(Node *o, int x) {
int res = -;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == ) res = o->v;
if(d == -) d = ;
}
return res;
} int suf(Node *o, int x) {
int res = -;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == ) res = o->v;
if(d == -) d = ;
}
return res;
} int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif pis = pool;
int n, opt, x;
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%d%d", &opt, &x);
if(opt == ) insert(x);
else if(opt == ) remove(root, x);
else if(opt == ) printf("%d\n", rank(root, x));
else if(opt == ) printf("%d\n", kth(root, x));
else if(opt == ) printf("%d\n", pre(root, x));
else printf("%d\n", suf(root, x));
} return ;
}
打删除标记的还没想好前驱后继怎么求TAT
斜堆,非旋转treap,替罪羊树的更多相关文章
- [模板] 平衡树: Splay, 非旋Treap, 替罪羊树
简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...
- 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)
在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...
- 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树
直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势: AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...
- 左偏树 / 非旋转treap学习笔记
背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- BZOJ3224普通平衡树——非旋转treap
题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...
- 非旋转Treap
Treap是一种平衡二叉树,同时也是一个堆.它既具有二叉查找树的性质,也具有堆的性质.在对数据的查找.插入.删除.求第k大等操作上具有期望O(log2n)的复杂度. Treap可以通过节点的旋 ...
- 旋转/非旋转treap的简单操作
treap(树堆) 是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树: 为什么可以这样呢? 因为在维护堆的时候可以同时保证搜索树的性质: (比如当一棵树的一个域满足 ...
- 非旋转Treap:用运行时间换调试时间的有效手段
非旋转Treap:用运行时间换调试时间的有效手段 Hello大家好,我们今天来聊一聊非旋转Treap. 相信各位或多或少都做过些序列上的问题.如果水题我们考虑暴力:不强制在线我们可能用过莫队和待修 ...
- 关于非旋转Treap
刚刚跟着EM-LGH大佬学了非旋转Treap 非常庆幸不用再写万恶的rotate了(来自高级数据结构的恶意) 来记一下 Treap 概念 简单来说,\(Tree_{二叉搜索树} * Heap_堆 = ...
随机推荐
- 继承语法含有main()方法
package me.ybleeho; class Cleanser{ //清洁剂 private String s="Cleanser"; public void append( ...
- slf4j与log4j
推荐使用SLF4J(Simple Logging Facade for Java)作为日志的api,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统. ...
- 完美让IE兼容input placeholder属性的jquery实现
调用时直接引用jquery与下面的js就行了,相对网上的大多数例子来说,这个是比较完美的方案. /* * 球到西山沟 * http://www.cnzj5u.com * 2014/11/26 12:1 ...
- spring与axis2整合发布webservice
最近在研究整合spring框架和axis2发布webservice服务,由于本人也才学java不久,为了便于以后的查看,在这里记录下发布过程. 所需的工具包,spring.jar和axis2链接地址为 ...
- css3基础教程十六变形与动画animation
前面我们讲过的变形与动画一般都是通过鼠标的单击.获得焦点,被点击或对元素进行一定改变后以后触发效果的,那么有没有像Flash一样自动播放的动画效果呢?答案当然是肯定的,这就是我们今天要讲到的anima ...
- ios开发相关网站
1.苹果开发者中心(ios Dev Center):最权威的学习ios开发的地方,提供ios开发所能用到的所有内容(包含文档.指南以及实例代码). https://developer.apple.co ...
- nginx 环境搭建使用之入门
1.http://nginx.org/下载最新的nginx 现在最新的版本是nginx-1.9.1 下载.tar.gz包 ,解压. timeless@timeless-HP-Pavilion-g4 ...
- jsp注释方式
1,HTML的注释方法 <!--...add your comments here...--> 说明:使用该注释方法,其中的注释内容在客户端浏览中是看不见的.但是查看源代码时,客户是可以看 ...
- Inter系列处理器名称浅析
东拼西凑之作,仅仅整理而已,望周知 ------------------------------------------------------------------ 举例 CPU酷睿i5-3230 ...
- iOS-MVVM--备用
我会修改一个关键架构,并将其带入我从那时起就在开发的各种应用,即使用一种叫做 Model-View-ViewModel 的架构替换 Model-View-Controller. 所以,MVVM 到底是 ...