- $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. 多线程在javaweb中的应用

    1.web应用中,要对某一个任务用多线程实现,最简单的代码格式是不是必须把要执行的代码放在run方法中? WEB服务器会帮你把每个访问请求开辟一个线程,你只要按照你所开发的框架,比如tomcat会让你 ...

  2. [BZOJ4653][NOI2016]区间 贪心+线段树

    4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MB Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],. ...

  3. 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告

    P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...

  4. mysql命令修改登录用户密码

    方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password(‘新密码’); 例子:my ...

  5. Creating a Cron Job in K8S

    Creating a Cron Job Cron jobs require a config file. This example cron job config .spec file prints ...

  6. windows中apache+tomcat整合,使php和java项目能够独立运行

    一.下载和安装 1.安装php  网上有安装教程,不再赘述 2.安装apache 比如安装目录为e:\apache;  项目根目录为e:\www;   网上有安装教程,不再赘述 3.安装jdk  不再 ...

  7. tomcat maven插件启动报错tomcat-users.xml cannot be read

    tomcat maven插件启动报错tomcat-users.xml cannot be read [ERROR] Failed to execute goal org.codehaus.mojo:t ...

  8. 前端PHP入门-017-系统内置函数-会查阅API

    大家要知道未来这就是你的工具 如果代码能赚钱,帮你赚钱最多的是基本语法 如果你还在当程序员,你最好的伙伴就是手册 问问你自己:你有CSS2.0/3.0手册,JavaScript手册,Jquery手册, ...

  9. [吴恩达机器学习笔记]12支持向量机1从逻辑回归到SVM/SVM的损失函数

    12.支持向量机 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考资料 斯坦福大学 2014 机器学习教程中文笔记 by 黄海广 12.1 SVM损失函数 从逻辑回归到支持向量机 为了描述 ...

  10. Vue DatePicker和不可用

    input控件可以直接设置disabled为不可用状态: DatePicker要想不可编辑,设置readonly属性即可 <DatePicker type="date" v- ...