$fhqTreap$
- $fhqTreap$与$Treap$的差异
$fhqTreap$是$Treap$的非旋版本,可以实现一切$Treap$操作,及区间操作和可持久化
$fhqTreap$非旋关键在于分裂与合并$(Split \ \& \ Merge)$
- $Split$
分裂相当于将一棵平衡树分为两棵平衡树,比如可以以值$x$为分界线,即分为一棵权值均$\le x$的平衡树,及一棵权值均$> x$的平衡树
对于实现,有$A$树根$rtA$及$B$树根$rtB$,若当前结点$root$权值$\le x$,则$root$左子树均属于$A$树,$>$同理
void Split (int root, int mid, int& rtA, int& rtB) {
if (! root) {
rtA = rtB = ;
return ;
}
if (Val[root] <= mid)
rtA = root, Split (Son[root][], mid, Son[root][], rtB);
else
rtB = root, Split (Son[root][], mid, rtA, Son[root][]);
update (root);
}
- $Merge$
将两棵树合并为一棵,需要既满足$BST$亦满足$Heap$,又$A$树的权值均小于$B$树,所以$BST$性质很好满足,那么再判一下随机的$rank$大小就好了
如果$A$树结点$node_a$的$rank$小于$B$树的结点$node_b$的$rank$,那么$node_b$必然是$node_a$的右子节点,反之$node_a$必然是$node_b$的左子结点
int Merge (int rtA, int rtB) {
if (! rtA || ! rtB)
return rtA + rtB;
if (Rank[rtA] < Rank[rtB]) {
Son[rtA][] = Merge (Son[rtA][], rtB);
update (rtA);
return rtA;
}
else {
Son[rtB][] = Merge (rtA, Son[rtB][]);
update (rtB);
return rtB;
}
}
- 单点操作
- $Insert$
将树以插入结点权值$x$分裂,在权值均$\le x$的$A$树中插入即可
void Insert (int x) {
int rtA, rtB;
Split (Root, x, rtA, rtB);
Root = Merge (Merge (rtA, newnode (x)), rtB);
}
- $Delete$
将树以$x$分裂为$A$与$B$,再将$A$以$x - 1$为基准继续分裂为$C$与$D$,那么将$D$根节点的左右子节点合并即可
void Delete (int x) {
int rtA, rtB, rtC;
Split (Root, x, rtA, rtB);
Split (rtA, x - , rtA, rtC);
rtC = Merge (Son[rtC][], Son[rtC][]);
Root = Merge (Merge (rtA, rtC), rtB);
}
- $Query\_Rank$
int Query_Rank (int x) {
int rtA, rtB;
Split (Root, x - , rtA, rtB);
int rank = Size[rtA] + ;
Root = Merge (rtA, rtB);
return rank;
}
- $Query\_Kth$
int Query_Kth (int root, int k) {
while (true) {
if (Size[Son[root][]] >= k)
root = Son[root][];
else {
k -= Size[Son[root][]] + ;
if (! k)
return Val[root];
root = Son[root][];
}
}
}
- 前驱结点
将树以$x - 1$分裂,那么树$A$的最后一名即为该结点的前驱结点
int Query_Prenode (int x) {
int rtA, rtB;
Split (Root, x - , rtA, rtB);
int pre = Query_Kth (rtA, Size[rtA]);
Root = Merge (rtA, rtB);
return pre;
}
- 后继结点
int Query_Nextnode (int x) {
int rtA, rtB;
Split (Root, x, rtA, rtB);
int nxt = Query_Kth (rtB, );
Root = Merge (rtA, rtB);
return nxt;
}
- 完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <algorithm> using namespace std; const int MAXN = 1e05 + ; int Son[MAXN][]= {};
int Size[MAXN]= {};
int Val[MAXN]= {};
int Rank[MAXN]= {};
int nodes = ; int Root = ;
int newnode (int val) {
int root = ++ nodes;
Size[root] = ;
Val[root] = val;
Rank[root] = rand ();
return root;
} void update (int root) {
Size[root] = Size[Son[root][]] + Size[Son[root][]] + ;
} void Split (int root, int mid, int& rtA, int& rtB) {
if (! root) {
rtA = rtB = ;
return ;
}
if (Val[root] <= mid)
rtA = root, Split (Son[root][], mid, Son[root][], rtB);
else
rtB = root, Split (Son[root][], mid, rtA, Son[root][]);
update (root);
} int Merge (int rtA, int rtB) {
if (! rtA || ! rtB)
return rtA + rtB;
if (Rank[rtA] < Rank[rtB]) {
Son[rtA][] = Merge (Son[rtA][], rtB);
update (rtA);
return rtA;
}
else {
Son[rtB][] = Merge (rtA, Son[rtB][]);
update (rtB);
return rtB;
}
} void Insert (int x) {
int rtA, rtB;
Split (Root, x, rtA, rtB);
Root = Merge (Merge (rtA, newnode (x)), rtB);
} void Delete (int x) {
int rtA, rtB, rtC;
Split (Root, x, rtA, rtB);
Split (rtA, x - , rtA, rtC);
rtC = Merge (Son[rtC][], Son[rtC][]);
Root = Merge (Merge (rtA, rtC), rtB);
} int Query_Rank (int x) {
int rtA, rtB;
Split (Root, x - , rtA, rtB);
int rank = Size[rtA] + ;
Root = Merge (rtA, rtB);
return rank;
} int Query_Kth (int root, int k) {
while (true) {
if (Size[Son[root][]] >= k)
root = Son[root][];
else {
k -= Size[Son[root][]] + ;
if (! k)
return Val[root];
root = Son[root][];
}
}
} int Query_Prenode (int x) {
int rtA, rtB;
Split (Root, x - , rtA, rtB);
int pre = Query_Kth (rtA, Size[rtA]);
Root = Merge (rtA, rtB);
return pre;
} int Query_Nextnode (int x) {
int rtA, rtB;
Split (Root, x, rtA, rtB);
int nxt = Query_Kth (rtB, );
Root = Merge (rtA, rtB);
return nxt;
} int M; int getnum () {
int num = ;
char ch = getchar ();
int flag = ; while (! isdigit (ch)) {
if (ch == '-')
flag = ;
ch = getchar ();
}
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return flag ? - num : num;
} int main () {
// freopen ("Input.txt", "r", stdin); srand (time (NULL)); M = getnum ();
for (int Case = ; Case <= M; Case ++) {
int opt = getnum ();
int x, k;
int rank, pre, nxt;
switch (opt) {
case :
x = getnum ();
Insert (x);
break;
case :
x = getnum ();
Delete (x);
break;
case :
x = getnum ();
rank = Query_Rank (x);
printf ("%d\n", rank);
break;
case :
k = getnum ();
x = Query_Kth (Root, k);
printf ("%d\n", x);
break;
case :
x = getnum ();
pre = Query_Prenode (x);
printf ("%d\n", pre);
break;
case :
x = getnum ();
nxt = Query_Nextnode (x);
printf ("%d\n", nxt);
break;
}
} return ;
} /*
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
*/
fhqTreap
- 区间操作
- 建树
类似线段树的建法即可
int Build (int left, int right) {
if (left > right)
return ;
int mid = (left + right) >> ;
int root = newnode (mid - );
Son[root][] = Build (left, mid - );
Son[root][] = Build (mid + , right);
update (root);
return root;
}
- 区间操作
将树以$R$分裂,再将$A$树以$L - 1$分裂,那么得到的树$D$,就恰好包含了区间$[L, R]$,直接操作即可
int rtA, rtB;
int rtC, rtD;
Split (Root, R, rtA, rtB);
Split (rtA, L - , rtC, rtD);
// 以下为操作
- 完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <algorithm> using namespace std; const int MAXN = 1e05 + ; int Son[MAXN][]= {};
int Size[MAXN]= {};
int Val[MAXN]= {};
int Rank[MAXN]= {};
int Revtag[MAXN]= {};
int nodes = ; int Root = ;
int newnode (int x) {
int root = ++ nodes;
Size[root] = ;
Val[root] = x;
Rank[root] = rand ();
Revtag[root] = ;
return root;
} void update (int root) {
Size[root] = Size[Son[root][]] + Size[Son[root][]] + ;
} int Build (int left, int right) {
if (left > right)
return ;
int mid = (left + right) >> ;
int root = newnode (mid - );
Son[root][] = Build (left, mid - );
Son[root][] = Build (mid + , right);
update (root);
return root;
} void pushdown (int root) {
if (Revtag[root]) {
swap (Son[root][], Son[root][]);
Revtag[Son[root][]] ^= ;
Revtag[Son[root][]] ^= ;
Revtag[root] = ;
}
} void Split (int root, int mid, int& rtA, int& rtB) {
if (! root) {
rtA = rtB = ;
return ;
}
pushdown (root);
if (Size[Son[root][]] >= mid)
rtB = root, Split (Son[root][], mid, rtA, Son[root][]);
else
rtA = root, Split (Son[root][], mid - Size[Son[root][]] - , Son[root][], rtB);
update (root);
} int Merge (int rtA, int rtB) {
if (! rtA || ! rtB)
return rtA + rtB;
pushdown (rtA), pushdown (rtB);
if (Rank[rtA] < Rank[rtB]) {
Son[rtA][] = Merge (Son[rtA][], rtB);
update (rtA);
return rtA;
}
else {
Son[rtB][] = Merge (rtA, Son[rtB][]);
update (rtB);
return rtB;
}
} void Reverse (int L, int R) {
int rtA, rtB;
int rtC, rtD;
Split (Root, R, rtA, rtB);
Split (rtA, L - , rtC, rtD);
Revtag[rtD] ^= ;
rtA = Merge (rtC, rtD);
Root = Merge (rtA, rtB);
} int N, M; void Output (int root) {
pushdown (root);
if (Son[root][])
Output (Son[root][]);
if (Val[root] >= && Val[root] <= N)
printf ("%d ", Val[root]);
if (Son[root][])
Output (Son[root][]);
} int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int main () {
srand (time (NULL));
N = getnum (), M = getnum ();
Root = Build (, N + );
for (int Case = ; Case <= M; Case ++) {
int l = getnum (), r = getnum ();
l ++, r ++;
Reverse (l, r);
}
Output (Root);
puts (""); return ;
} /*
5 1
1 3
*/ /*
5 3
1 3
1 3
1 4
*/
区间翻转操作
- 可持久化
直接类似一般的可持久化实现,主要修改在$Split$与$Merge$操作
#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <algorithm> using namespace std; const int MAXN = 5e05 + ;
const int MAXLOG = + ; int Son[MAXN * MAXLOG][]= {};
int Size[MAXN * MAXLOG]= {};
int Val[MAXN * MAXLOG]= {};
int Rank[MAXN * MAXLOG]= {};
int nodes = ; int Root[MAXN]= {};
int newnode (int val) {
int root = ++ nodes;
Size[root] = ;
Val[root] = val;
Rank[root] = rand ();
return root;
} void update (int root) {
Size[root] = Size[Son[root][]] + Size[Son[root][]] + ;
} void copy (int pre, int p) {
Son[p][] = Son[pre][], Son[p][] = Son[pre][];
Size[p] = Size[pre];
Val[p] = Val[pre];
Rank[p] = Rank[pre];
} void Split (int pre, int mid, int& rtA, int& rtB) {
if (! pre) {
rtA = rtB = ;
return ;
}
if (Val[pre] <= mid) {
rtA = ++ nodes;
copy (pre, rtA);
Split (Son[pre][], mid, Son[rtA][], rtB);
update (rtA);
}
else {
rtB = ++ nodes;
copy (pre, rtB);
Split (Son[pre][], mid, rtA, Son[rtB][]);
update (rtB);
}
} int Merge (int prertA, int prertB) {
if (! prertA || ! prertB)
return prertA + prertB;
if (Rank[prertA] < Rank[prertB]) {
int rtA = ++ nodes;
copy (prertA, rtA);
Son[rtA][] = Merge (Son[prertA][], prertB);
update (rtA);
return rtA;
}
else {
int rtB = ++ nodes;
copy (prertB, rtB);
Son[rtB][] = Merge (prertA, Son[prertB][]);
update (rtB);
return rtB;
}
} void Insert (int& proot, int x) {
int rtA, rtB;
Split (proot, x, rtA, rtB);
proot = Merge (Merge (rtA, newnode (x)), rtB);
} void Delete (int& proot, int x) {
int rtA, rtB, rtC;
Split (proot, x, rtA, rtB);
Split (rtA, x - , rtA, rtC);
rtC = Merge (Son[rtC][], Son[rtC][]);
proot = Merge (Merge (rtA, rtC), rtB);
} int Query_Rank (int& proot, int x) {
int rtA, rtB;
Split (proot, x - , rtA, rtB);
int rank = Size[rtA] + ;
proot = Merge (rtA, rtB);
return rank;
} int Query_Kth (int root, int k) {
while (true) {
if (Size[Son[root][]] >= k)
root = Son[root][];
else {
k -= Size[Son[root][]] + ;
if (! k)
return Val[root];
root = Son[root][];
}
}
} int Query_Prenode (int& proot, int x) {
int rtA = , rtB;
Split (proot, x - , rtA, rtB);
if (! rtA)
return - ;
int pre = Query_Kth (rtA, Size[rtA]);
proot = Merge (rtA, rtB);
return pre;
} int Query_Nextnode (int& proot, int x) {
int rtA, rtB = ;
Split (proot, x, rtA, rtB);
if (! rtB)
return ;
int nxt = Query_Kth (rtB, );
proot = Merge (rtA, rtB);
return nxt;
} int M; int getnum () {
int num = ;
char ch = getchar ();
int flag = ; while (! isdigit (ch)) {
if (ch == '-')
flag = ;
ch = getchar ();
}
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return flag ? - num : num;
} int main () {
// freopen ("Input.txt", "r", stdin); srand (time (NULL));
M = getnum ();
for (int Case = ; Case <= M; Case ++) {
Root[Case] = Root[getnum ()];
int opt = getnum ();
int x, k;
int rank, pre, nxt;
switch (opt) {
case :
x = getnum ();
Insert (Root[Case], x);
break;
case :
x = getnum ();
Delete (Root[Case], x);
break;
case :
x = getnum ();
rank = Query_Rank (Root[Case], x);
printf ("%d\n", rank);
break;
case :
k = getnum ();
x = Query_Kth (Root[Case], k);
printf ("%d\n", x);
break;
case :
x = getnum ();
pre = Query_Prenode (Root[Case], x);
printf ("%d\n", pre);
break;
case :
x = getnum ();
nxt = Query_Nextnode (Root[Case], x);
printf ("%d\n", nxt);
break;
}
} return ;
} /*
10
0 1 9
1 1 3
1 1 10
2 4 2
3 3 9
3 1 2
6 4 1
6 2 9
8 6 3
4 5 8
*/
可持久化平衡树
随机推荐
- [二十三]SpringBoot 之 redis
本文章牵涉到的技术点比较多:spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的了解或者也可以先看看这篇文章 ...
- Repository HDU - 2846 (trie)
题中没给范围 所以控制不好数组范围..不是超内存就是runtime.. 好吧 到了晚上终于调出来数组模拟的了 题意: 求含有某字符段的个数 解析: 把每个字符串遍历一遍 以每个元素为起点建树就好了.. ...
- Astronauts UVALive - 3713(2-SAT)
大白书例题 #include <iostream> #include <cstdio> #include <sstream> #include <cstrin ...
- c# 字符串转Byte[],如何将Byte[]插入到Oracle Blob
byte[] xx=Encoding.Default.GetBytes("12121232"); 插入数据库 string sqlStr = "update sys_ta ...
- 【比赛】HNOI2018 游戏
考试的时候线段树区间查询的return条件打成了l==r....于是光荣爆20(线段树都不会打了?) 看膜博士的题解 #include<bits/stdc++.h> #define ui ...
- mysql数据库的存储过程
一. 什么是存储过程: 存储过程是一组可编程的函数,是为了完成特定功能的SQL语句集,经过第一次编译后再次调用不需要再次编译,创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调 ...
- 【BZOJ4197】【NOI2015】寿司晚宴(动态规划)
[BZOJ4197][NOI2015]寿司晚宴(动态规划) 题面 BZOJ 从\([2,n]\)中选择两个集合(可以为空集),使得两个集合中各选一个数出来,都互质. 求方案数. 题解 对于\(500\ ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告
P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...
- 【数学】【背包】【NOIP2018】P5020 货币系统
传送门 Description 在网友的国度中共有 \(n\) 种不同面额的货币,第 \(i\) 种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张.为了方便,我们把货币种数为 \(n ...
- 关于使用EmguCV出现 “无法加载 DLL“cvextern”: 找不到指定的程序” 的解决方法
http://blog.csdn.net/cdjcong/article/details/8444191 查找了网上的一些说法,都是说没有设置好路径,或者未将DLL文件复制到Debug文件夹下,但是我 ...