- $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. document.readyState的使用

    document.readyState:判断文档是否加载完成.firefox不支持. 这个属性是只读的,传回值有以下的可能: 0-UNINITIALIZED:XML 对象被产生,但没有任何文件被加载. ...

  2. BZOJ1996 HNOI2010合唱队(区间dp)

    设f[i][j][0/1]表示i~j这段区间上一次选择的是最左/最右人的方案数.转移显然. #include<iostream> #include<cstdio> #inclu ...

  3. Proving Equivalences UVALive - 4287(强连通分量 水题)

    就是统计入度为0 的点 和 出度为0 的点  输出 大的那一个,, 若图中只有一个强连通分量 则输出0即可 和https://www.cnblogs.com/WTSRUVF/p/9301096.htm ...

  4. 【题解】AC自动机题解合集

    最近貌似大家都在搞字符串?很长一段时间都没有写博客了……还是补一补坑吧. 感觉AC自动机真的非常优美了,通过在trie树上建立fail指针可以轻松解决多模匹配的问题.实际上在AC自动机上的匹配可以看做 ...

  5. 【数组】- ArrayList自动扩容机制

    不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...

  6. 洛谷 P2233 [HNOI2002]公交车路线 解题报告

    P2233 [HNOI2002]公交车路线 题目背景 在长沙城新建的环城公路上一共有8个公交站,分别为A.B.C.D.E.F.G.H.公共汽车只能够在相邻的两个公交站之间运行,因此你从某一个公交站到另 ...

  7. 深入详解windows安全认证机制ntlm&Kerberos

    0x01 为什么要理解windows 安全认证机制: 加深对后续各种漏洞利用的理解深度,还是那句话,要知其然,更要知其所以然,不废话,咱们直接开始 0x02 windows认证协议主要有以下两种: 基 ...

  8. dfs序七个经典问题(转)

    我这个人不怎么喜欢写轻重链剖分和LCT 还是喜欢dfs序.括号序列之类的 毕竟线段树好写多了 然后就有了这篇转载的文章 写在这边以后有时间看看 原文链接:https://www.cnblogs.com ...

  9. eclipse show view失效的解决办法

    今天打开eclipse,发现console窗口没有了,然后使用show view也无法打开,上网查找办法,找到了方法试了一下,窗口重置(Windows-->Perspective-->Re ...

  10. Codeforces 894.C Marco and GCD Sequence

    C. Marco and GCD Sequence time limit per test 1 second memory limit per test 256 megabytes input sta ...