$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
*/
可持久化平衡树
随机推荐
- l洛谷 (水题)P4144 大河的序列
题目戳 Solution: 这题前面都是废话,关键的一句就是本题求的是序列中连续一段的相与值(&)+相或值(|)最大,然后对这个值进行快速幂取模.考虑到两个数相与最大能得到的就是这两个数中的最 ...
- 【BZOJ3166】ALO(主席树)
[BZOJ3166]ALO(主席树) 题面 权限题qwq 资磁洛谷 题解 用一个\(set\)求出左右侧比这个数大的第\(2\)个数, 然后用可持久化\(Trie\)算一下就好啦 #include&l ...
- LCA的倍增算法
LCA,即树上两点之间的公共祖先,求这样一个公共祖先有很多种方法: 暴力向上:O(n) 每次将深度大的点往上移动,直至二者相遇 树剖:O(logn) 在O(2n)预处理重链之后,每次就将深度大的沿重链 ...
- 基于线程池技术的web服务器
前言:首先简单模拟一个场景,前端有一个输入框,有一个按钮,点击这个按钮可以实现搜索输入框中的相关的文本和图片(类似于百度.谷歌搜索).看似一个简单的功能,后端处理也不难,前端发起一个请求,后端接受到这 ...
- 图像处理之Canny边缘检测
http://blog.csdn.net/jia20003/article/details/41173767 图像处理之Canny 边缘检测 一:历史 Canny边缘检测算法是1986年有John F ...
- 洛谷P3414 SAC#1 - 组合数
P3414 SAC#1 - 组合数 218通过 681提交 题目提供者ProjectWTA 标签 难度普及/提高- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 讨论区出bug ...
- HDU 6194 后缀数组
string string string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- 赤池信息量准则 ( Akaike information criterion)
sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
- 常用的20个强大的 Sublime Text 插件
作为一个开发者你不可能没听说过 Sublime Text.不过你没听说过也没关系,下面让你明白. Sublime Text是一款非常精巧的文本编辑器,适合编写代码.做笔记.写文章.它用户界面十分整洁, ...
- JavaScript-变量与作用域链
jQuery片段: 1 var 2 // Will speed up references to window, and allows munging its name. 3 win ...