- $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
*/

可持久化平衡树

随机推荐

  1. 洛谷 P3177 树上染色

    题面 题目要求将k个点染成黑色,求黑点两两距离及白点两两距离,使他们之和最大. 我们可以将距离转化为路径,然后再将路径路径拆分成边,就可以记录每条边被经过的次数,直接计算即可. 很简单对吧?那么问题来 ...

  2. Java操作excel(POI)

    由于在项目中使用了将excel数据导入到数据库.在这里分享一下. 这里使用的POI方式,支持两种格式(xls,xlsx) package com.entity; import java.io.File ...

  3. 【Codeforces 98E】 Help Shrek and Donkey 游戏策略神题

    from http://www.cnblogs.com/MashiroSky/p/6576398.html A君有n张牌,B君有m张牌,桌上还有一张反扣着的牌,每张牌都不一样. 每个回合可以做两件事中 ...

  4. python基础(3)

    使用list和tuple list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: ...

  5. 【agc003E】Sequential operations on Sequence

    Portal -->agc003E Description 给你一个数串\(S\),一开始的时候\(S=\{1,2,3,...,n\}\),现在要对其进行\(m\)次操作,每次操作给定一个\(a ...

  6. 【CodeChef】Chef and Graph Queries

    Portal --> CC Chef and Graph Queries Solution 快乐数据结构题(然而好像有十分优秀的莫队+可撤销并查集搞法qwq) 首先考虑一种方式来方便一点地..计 ...

  7. CSU 多校训练第二场 J Pinemi Puzzles

    传送门:http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=2279 题意: 代码: #include <set> #incl ...

  8. 9.Android UiAutomator正则表达式的使用

    一.正则表达式元字符: 1.一些常用元字符: 元字符 描述 . 表示任意一个字符 \s 空格字符(空格键.tab.换行.换页.回车) \S 非空字符串([^\s]) \d 一个数字(相当于[0-9]中 ...

  9. 一元回归_R相关系数_多重检验

     sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  10. Java运行原理研究(未完待续)

    java的介绍和定性 java的优缺点分析 jdk的组成结构 jvm的工作原理 java的跨平台原理 java的编译和运行过程