斜堆,非旋转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_堆 = ...
随机推荐
- spring data jpa Specification 例子
/** * 封装查询条件 * * @param baseQueryDTO * @return */ private Specification<ActivityBase> getSpeci ...
- javascript——基本包装类型
<script type="text/javascript"> //1.Boolean 类型 //2.Number 类型 //3.String 类型 //Boolean ...
- Excel等外部程序点击链接会带上IE信息的bug
今天碰到一个问题,在Excel内点击链接到默认浏览器Chrome打开,奇怪的是服务端收到的Session一直对不上. 查了很久发现这个Excel到Chrome的跳转竟然带上了IE的Cookie 和 U ...
- 项目知识点.Part2
1. 取消collectionView头视图重叠情况:以下两种情况效果一样 但是有一点点bug 每次remove之后 需要把视图刷到上面才会显示(后续会改进方法) for (UIView *view ...
- SQL语句の集锦
6.删除数据后根据主键从备份表中恢复 insert sameTable_1 (name,dz) select name,dz from sameTable_1_bak where not exist ...
- SQL语句操作大全
SQL语句操作大全 本文分为以下六个部分: 基础部分 提升部分 技巧部分 数据开发–经典部分 SQL Server基本函数部分 常识部分 一.基础 1.说明:创建数据库CREATE DATABAS ...
- Tag Helpers 介绍
Tag Helpers 介绍 原文:Introduction to Tag Helpers作者:Rick Anderson翻译:刘浩杨校对:高嵩(Jack) 什么是 Tag Helpers? Tag ...
- BZOJ 1072 排列
Description 给一个数字串\(s\)和正整数\(d\), 统计\(s\)有多少种不同的排列能被\(d\)整除(可以有前导\(0\)).例如\(123434\)有\(90\)种排列能被\(2\ ...
- XML CDATA
/* <![CDATA[ */var mv_dynamic_to_top = {"text":"To Top","version":& ...
- cf B. Number Busters
http://codeforces.com/contest/382/problem/B 题意:给你Aa,b,w,x,c,然后每经过1秒,c=c-1; 如果b>=x,b=b-x;否则 a=a-1 ...