FHQ-treap,即无旋 treap,又称分裂合并 treap,支持维护序列,可持久化等特性。

FHQ-treap 有两个核心操作,分裂合并。通过这两个操作,在很多情况下可以比旋转 treap 等方便的实现一些操作。

FHQ-treap 与其他的平衡树相比,他最明显的优点是:它好写!!!,想象一下,在考场上,你用较短的时间写出 FHQ-treap 和花很长时间敲 Splay,还得琢磨到底怎么旋转,优势就体现的很明显了,它和 treap 相比,它可以更好的进行区间操作,接下来将一一介绍。

平衡树也是一棵二叉搜索树。

结构体定义

定义

这里我们采取结构体来定义平衡树的节点,下面是最基本的节点信息。

  1. struct node {
  2. int val, pai, siz;
  3. int ls, rs;
  4. } t[N];

pai 是我们随机的一个值,val 是当前节点的权值,ls, rs 左右孩子,siz 是当前点的子树大小。

增加新节点

为了节省空间,我们一般会开一个“垃圾桶”来存储被删掉的节点的编号,要增加新节点时,如果垃圾桶里有节点,那么优先使用垃圾桶里的节点。回收利用很环保

  1. vector<int> rub;
  2. int newnod(int x) {
  3. int u;
  4. if (!rub.empty()) {
  5. u = rub.back();
  6. rub.pop_back();
  7. }
  8. else {
  9. u = ++ tot;
  10. }
  11. t[u].siz = 1;
  12. t[u].ls = t[u].rs = 0;
  13. t[u].val = x;
  14. t[u].pai = rand();
  15. return u;
  16. }

分裂

分裂有两种,一种是按照节点个数来分裂,另一种是按照权值大小来分裂。

一般最常用的是按照节点个数分裂,但是按照权值大小分裂也会用到。

一般进行操作,我们的通用方法是将被操作点单独分裂成一棵树,对这棵树进行操作。

按照节点数量分裂的代码。

  1. void split_rk(int u, int k, int &x, int &y) {
  2. if (u == 0) {
  3. x = y = 0;
  4. return ;
  5. }
  6. if (t[lc].siz + 1 <= k) {
  7. x = u;
  8. split_rk(rc, k - t[lc].siz - 1, t[u].rs, y);
  9. }
  10. else {
  11. y = u;
  12. split_rk(lc, k, x, t[u].ls);
  13. }
  14. pushup(u);
  15. }

按照权值分裂的代码。

  1. void split_val(int u, int v, int &x, int &y) { // x 和 y 是传参类型
  2. if (u == 0) {
  3. x = y = 0;
  4. return ;
  5. }
  6. if (t[u].val <= v) {
  7. x = u;
  8. split_val(rc, v, rc, y);
  9. }
  10. else {
  11. y = u;
  12. split_val(lc, v, x, lc);
  13. }
  14. pushup(u);
  15. }

合并

在旋转 treap 中,我们借助旋转操作来维护堆的性质,同时旋转时还不能改变树的性质。在无旋 treap 中,我们用合并达到相同的效果。

因为两个 treap 已经有序,所以我们在合并的时候只需要考虑把哪个树「放在上面」,把哪个「放在下面」,也就是是需要判断将哪个一个树作为子树。显然,根据堆的性质,我们需要把 \(pai\) 小的放在上面(这里采用小根堆)。

同时,我们还需要满足搜索树的性质。设 \(u < v\),若 \(u\) 的 \(pai\) 小于 \(v\) 的,那么 \(u\) 即为新根结点,并且 \(v\) 因为值比 \(u\) 更大,应与 \(u\) 的右子树合并;反之,则 \(v\) 作为新根结点,然后因为 \(u\) 的值比 \(v\) 小,与 \(v\) 的左子树合并。

  1. int Merge(int x, int y) {
  2. if (!x || !y) {
  3. return x + y;
  4. }
  5. if (t[x].pai < t[y].pai) {
  6. t[x].rs = Merge(t[x].rs, y);
  7. pushup(x);
  8. return x;
  9. }
  10. else {
  11. t[y].ls = Merge(x, t[y].ls);
  12. pushup(y);
  13. return y;
  14. }
  15. }

基本操作

插入

将新节点要插入的位置分裂出来,然后合并即可。

  1. void Insert(int x) {
  2. int u = newnod(x);
  3. int t1, t2;
  4. split_val(rt, x, t1, t2);
  5. rt = Merge(Merge(t1, u), t2);
  6. }

删除

将要删除的节点分裂出来,将两边的子树合并即可。

  1. void Erase(int x) {
  2. int t1, t2, t3;
  3. split_val(rt, x - 1, t1, t2);
  4. split_val(t2, x, t2, t3);
  5. rub.emplace_back(t2);
  6. t2 = Merge(t[t2].ls, t[t2].rs);
  7. rt = Merge(Merge(t1, t2), t3);
  8. }

查找排名

将要查找的点按照权值分裂出来,前面分裂出去的树的大小 \(+ 1\) 就是排名。

  1. int getrank(int x) {
  2. int t1, t2, rk;
  3. split_val(rt, x - 1, t1, t2);
  4. rk = t[t1].siz + 1;
  5. rt = Merge(t1, t2);
  6. ans ^= rk;
  7. las = rk;
  8. return rk;
  9. }

查找排名为 \(x\) 的节点的权值

将要查找的点按照节点个数分裂出来,进行操作。

  1. int getval(int x) {
  2. int t1, t2, t3, val;
  3. split_rk(rt, x - 1, t1, t2);
  4. split_rk(t2, 1, t2, t3);
  5. val = t[t2].val;
  6. ans ^= val;
  7. las = val;
  8. Merge(Merge(t1, t2), t3);
  9. return val;
  10. }

查找前驱后继

利用分裂来查找。

  1. int pre(int x) {
  2. int t1, t2, t3, pre;
  3. split_val(rt, x - 1, t1, t2);
  4. split_rk(t1, t[t1].siz - 1, t1, t3);
  5. pre = t[t3].val;
  6. rt = Merge(Merge(t1, t3), t2);
  7. ans ^= pre;
  8. las = pre;
  9. return pre;
  10. }
  11. int nxt(int x) {
  12. int t1, t2, t3, nxt;
  13. split_val(rt, x, t1, t2);
  14. split_rk(t2, 1, t2, t3);
  15. nxt = t[t2].val;
  16. rt = Merge(Merge(t1, t2), t3);
  17. ans ^= nxt;
  18. las = nxt;
  19. return nxt;
  20. }

区间操作

P3391 【模板】文艺平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

区间翻转练习题

  1. /*
  2. The code was written by yifan, and yifan is neutral!!!
  3. */
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. typedef long long ll;
  7. #define lc t[u].ls
  8. #define rc t[u].rs
  9. template<typename T>
  10. inline T read() {
  11. T x = 0;
  12. bool fg = 0;
  13. char ch = getchar();
  14. while (ch < '0' || ch > '9') {
  15. fg |= (ch == '-');
  16. ch = getchar();
  17. }
  18. while (ch >= '0' && ch <= '9') {
  19. x = (x << 3) + (x << 1) + (ch ^ 48);
  20. ch = getchar();
  21. }
  22. return fg ? ~x + 1 : x;
  23. }
  24. const int N = 1e5 + 5;
  25. int n, m, rt, tot;
  26. vector<int> rub;
  27. struct node {
  28. int val, tag;
  29. int ls, rs, siz, pai;
  30. } t[N << 1];
  31. inline void pushup(int u) {
  32. t[u].siz = t[lc].siz + t[rc].siz + 1;
  33. }
  34. inline void pushdown(int u) {
  35. if (!t[u].tag) {
  36. return ;
  37. }
  38. if (lc) t[lc].tag ^= 1;
  39. if (rc) t[rc].tag ^= 1;
  40. swap(t[u].ls, t[u].rs);
  41. t[u].tag = 0;
  42. }
  43. int newnod(int x) {
  44. int u = ++ tot;
  45. t[u].siz = 1;
  46. t[u].ls = t[u].rs = t[u].tag = 0;
  47. t[u].val = x;
  48. t[u].pai = rand();
  49. return u;
  50. }
  51. void split_rk(int u, int k, int &x, int &y) {
  52. if (!u) {
  53. x = y = 0;
  54. return ;
  55. }
  56. pushdown(u);
  57. if (t[lc].siz + 1 <= k) {
  58. x = u;
  59. split_rk(rc, k - t[lc].siz - 1, rc, y);
  60. }
  61. else {
  62. y = u;
  63. split_rk(lc, k, x, lc);
  64. }
  65. pushup(u);
  66. }
  67. int Merge(int x, int y) {
  68. if (!x || !y) {
  69. return x + y;
  70. }
  71. if (t[x].pai < t[y].pai) {
  72. pushdown(x);
  73. t[x].rs = Merge(t[x].rs, y);
  74. pushup(x);
  75. return x;
  76. }
  77. else {
  78. pushdown(y);
  79. t[y].ls = Merge(x, t[y].ls);
  80. pushup(y);
  81. return y;
  82. }
  83. }
  84. void print(int u) {
  85. if (!u) return ;
  86. pushdown(u);
  87. print(t[u].ls);
  88. printf("%d ", t[u].val);
  89. print(t[u].rs);
  90. }
  91. int main() {
  92. srand(time(NULL));
  93. n = read<int>(), m = read<int>();
  94. for (int i = 1; i <= n; ++ i) {
  95. rt = Merge(rt, newnod(i));
  96. }
  97. for (int i = 1, l, r; i <= m; ++ i) {
  98. l = read<int>(), r = read<int>();
  99. int t1, t2, t3;
  100. split_rk(rt, l - 1, t1, t2);
  101. split_rk(t2, r - l + 1, t2, t3);
  102. t[t2].tag ^= 1;
  103. rt = Merge(t1, Merge(t2, t3));
  104. }
  105. print(rt);
  106. return 0;
  107. }

P2042 [NOI2005] 维护数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

区间操作的练习好题,涉及线段树操作。

  1. /*
  2. The code was written by yifan, and yifan is neutral!!!
  3. */
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. typedef long long ll;
  7. #define lc (t[u].ls)
  8. #define rc (t[u].rs)
  9. template<typename T>
  10. inline T read() {
  11. T x = 0;
  12. bool fg = 0;
  13. char ch = getchar();
  14. while (ch < '0' || ch > '9') {
  15. fg |= (ch == '-');
  16. ch = getchar();
  17. }
  18. while (ch >= '0' && ch <= '9') {
  19. x = (x << 3) + (x << 1) + (ch ^ 48);
  20. ch = getchar();
  21. }
  22. return fg ? ~x + 1 : x;
  23. }
  24. const int N = 1e6 + 6;
  25. mt19937 rnd(time(0));
  26. int n, m, tot, rt;
  27. int a[N];
  28. vector<int> rub;
  29. struct node {
  30. int pai, ls, rs, siz;
  31. ll val, sum, mx, maxpre, maxlas, tag;
  32. bool tag1, tag2;
  33. } t[N];
  34. int New(int x) {
  35. int u;
  36. if (!rub.empty()) {
  37. u = rub.back();
  38. rub.pop_back();
  39. } else {
  40. u = ++ tot;
  41. }
  42. t[u].sum = t[u].val = (t[u].mx = x);
  43. t[u].maxpre = t[u].maxlas = max(0, x);
  44. t[u].siz = 1;
  45. t[u].pai = rnd();
  46. t[u].tag1 = t[u].tag2 = (t[u].tag = 0);
  47. t[u].ls = t[u].rs = 0;
  48. return u;
  49. }
  50. void pushup(int u) {
  51. if (!u) return;
  52. t[u].siz = t[lc].siz + t[rc].siz + 1;
  53. t[u].sum = t[lc].sum + t[rc].sum + t[u].val;
  54. t[u].maxpre = max(max(t[lc].maxpre, t[lc].sum + t[u].val + t[rc].maxpre), 0ll);
  55. t[u].maxlas = max(max(t[rc].maxlas, t[rc].sum + t[u].val + t[lc].maxlas), 0ll);
  56. t[u].mx = max(0ll, t[lc].maxlas + t[rc].maxpre) + t[u].val;
  57. if (lc) t[u].mx = max(t[u].mx, t[lc].mx);
  58. if (rc) t[u].mx = max(t[u].mx, t[rc].mx);
  59. }
  60. void cover(int u, ll c) {
  61. t[u].val = t[u].tag = c;
  62. t[u].sum = t[u].siz * c;
  63. t[u].maxpre = t[u].maxlas = max(0ll, t[u].sum);
  64. t[u].mx = max(c, t[u].sum);
  65. t[u].tag1 = 1;
  66. }
  67. void Reverse(int u) {
  68. if (!u) return ;
  69. swap(lc, rc);
  70. swap(t[u].maxpre, t[u].maxlas);
  71. t[u].tag2 ^= 1;
  72. }
  73. void pushdown(int u) {
  74. if (!u) return ;
  75. if (t[u].tag2) {
  76. if (lc) {
  77. Reverse(lc);
  78. }
  79. if (rc) {
  80. Reverse(rc);
  81. }
  82. t[u].tag2 = 0;
  83. }
  84. if (t[u].tag1) {
  85. if (lc) {
  86. cover(lc, t[u].tag);
  87. }
  88. if (rc) {
  89. cover(rc, t[u].tag);
  90. }
  91. t[u].tag = t[u].tag1 = 0;
  92. }
  93. }
  94. void split(int u, int k, int &x, int &y) {
  95. if (!u) {
  96. x = y = 0;
  97. return;
  98. }
  99. pushdown(u);
  100. if (t[lc].siz < k) {
  101. x = u;
  102. split(rc, k - t[lc].siz - 1, rc, y);
  103. } else {
  104. y = u;
  105. split(lc, k, x, lc);
  106. }
  107. pushup(u);
  108. }
  109. int Merge(int x, int y) {
  110. if (!x || !y) {
  111. return x + y;
  112. }
  113. if (t[x].pai < t[y].pai) {
  114. pushdown(x);
  115. t[x].rs = Merge(t[x].rs, y);
  116. pushup(x);
  117. return x;
  118. } else {
  119. pushdown(y);
  120. t[y].ls = Merge(x, t[y].ls);
  121. pushup(y);
  122. return y;
  123. }
  124. }
  125. int add(int l, int r) {
  126. if (l != r) {
  127. int mid = (l + r) >> 1;
  128. return Merge(add(l, mid), add(mid + 1, r));
  129. }
  130. return New(a[l]);
  131. }
  132. void Erase(int u) {
  133. if (!u) return ;
  134. rub.emplace_back(u);
  135. if (lc) {
  136. Erase(lc);
  137. }
  138. if (rc) {
  139. Erase(rc);
  140. }
  141. }
  142. void print(int u) {
  143. if (!u) return ;
  144. pushdown(u);
  145. print(lc);
  146. print(rc);
  147. }
  148. int main() {
  149. n = read<int>(), m = read<int>();
  150. for (int i = 1; i <= n; ++ i) {
  151. a[i] = read<int>();
  152. }
  153. rt = Merge(rt, add(1, n));
  154. string op;
  155. for (int i = 1, t1, t2, t3; i <= m; ++ i) {
  156. cin >> op;
  157. if (op == "INSERT") {
  158. int pos = read<int>(), len = read<int>();
  159. split(rt, pos, t1, t2);
  160. for (int i = 1; i <= len; ++ i) {
  161. a[i] = read<int>();
  162. }
  163. rt = Merge(Merge(t1, add(1, len)), t2);
  164. }
  165. if (op == "DELETE") {
  166. int pos = read<int>(), len = read<int>();
  167. split(rt, pos - 1, t1, t2);
  168. split(t2, len, t2, t3);
  169. Erase(t2);
  170. rt = Merge(t1, t3);
  171. }
  172. if (op == "MAKE-SAME") {
  173. int pos = read<int>(), len = read<int>(), v = read<int>();
  174. split(rt, pos - 1, t1, t2);
  175. split(t2, len, t2, t3);
  176. cover(t2, v);
  177. rt = Merge(Merge(t1, t2), t3);
  178. }
  179. if (op == "REVERSE") {
  180. int pos = read<int>(), len = read<int>();
  181. split(rt, pos - 1, t1, t2);
  182. split(t2, len, t2, t3);
  183. Reverse(t2);
  184. rt = Merge(Merge(t1, t2), t3);
  185. }
  186. if (op == "GET-SUM") {
  187. int pos = read<int>(), len = read<int>();
  188. split(rt, pos - 1, t1, t2);
  189. split(t2, len, t2, t3);
  190. printf("%lld\n", t[t2].sum);
  191. rt = Merge(Merge(t1, t2), t3);
  192. }
  193. if (op == "MAX-SUM") {
  194. printf("%lld\n", t[rt].mx);
  195. }
  196. }
  197. return 0;
  198. }

P2596 [ZJOI2006] 书架 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

模板题

  1. /*
  2. The code was written by yifan, and yifan is neutral!!!
  3. */
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. typedef long long ll;
  7. #define lc t[u].ls
  8. #define rc t[u].rs
  9. template<typename T>
  10. inline T read() {
  11. T x = 0;
  12. bool fg = 0;
  13. char ch = getchar();
  14. while (ch < '0' || ch > '9') {
  15. fg |= (ch == '-');
  16. ch = getchar();
  17. }
  18. while (ch >= '0' && ch <= '9') {
  19. x = (x << 3) + (x << 1) + (ch ^ 48);
  20. ch = getchar();
  21. }
  22. return fg ? ~x + 1 : x;
  23. }
  24. const int N = 8e4 + 5;
  25. mt19937 rnd(time(0));
  26. int n, m, tot, rt;
  27. int Id[N];
  28. struct node {
  29. int val, siz, pai;
  30. int ls, rs, fa;
  31. } t[N << 1];
  32. void pushup(int u) {
  33. t[u].siz = t[lc].siz + t[rc].siz + 1;
  34. t[lc].fa = t[rc].fa = u;
  35. }
  36. int New(int x) {
  37. int u = ++ tot;
  38. t[u].siz = 1;
  39. t[u].val = x;
  40. t[u].pai = rnd();
  41. t[u].ls = t[u].rs = t[u].fa = 0;
  42. Id[x] = u;
  43. return u;
  44. }
  45. int Find(int u) {
  46. int res = t[t[u].ls].siz + 1;
  47. for (; u != rt; u = t[u].fa) {
  48. if (t[t[u].fa].rs == u) {
  49. res += t[t[t[u].fa].ls].siz + 1;
  50. }
  51. }
  52. return res;
  53. }
  54. void split_rk(int u, int k, int &x, int &y) {
  55. if (!u) {
  56. x = y = 0;
  57. return ;
  58. }
  59. if (t[lc].siz < k) {
  60. x = u;
  61. split_rk(rc, k - t[lc].siz - 1, rc, y);
  62. } else {
  63. y = u;
  64. split_rk(lc, k, x, lc);
  65. }
  66. pushup(u);
  67. }
  68. int Merge(int x, int y) {
  69. if (!x || !y) {
  70. return x + y;
  71. }
  72. if (t[x].pai < t[y].pai) {
  73. t[x].rs = Merge(t[x].rs, y);
  74. pushup(x);
  75. return x;
  76. } else {
  77. t[y].ls = Merge(x, t[y].ls);
  78. pushup(y);
  79. return y;
  80. }
  81. }
  82. int main() {
  83. n = read<int>(), m = read<int>();
  84. for (int i = 1; i <= n; ++ i) {
  85. rt = Merge(rt, New(read<int>()));
  86. }
  87. for (int i = 1, x, t1, t2, t3, t4; i <= m; ++ i) {
  88. string op;
  89. cin >> op;
  90. x = read<int>();
  91. if (op == "Top") {
  92. int k = Find(Id[x]);
  93. split_rk(rt, k - 1, t1, t2);
  94. split_rk(t2, 1, t2, t3);
  95. rt = Merge(Merge(t2, t1), t3);
  96. }
  97. if (op == "Bottom") {
  98. int k = Find(Id[x]);
  99. split_rk(rt, k - 1, t1, t2);
  100. split_rk(t2, 1, t2, t3);
  101. rt = Merge(Merge(t1, t3), t2);
  102. }
  103. if (op == "Insert") {
  104. int y = read<int>();
  105. int k = Find(Id[x]);
  106. if (y > 0) {
  107. split_rk(rt, k - 1, t1, t2);
  108. split_rk(t2, 1, t2, t3);
  109. split_rk(t3, y, t3, t4);
  110. rt = Merge(Merge(t1, t3), Merge(t2, t4));
  111. } else {
  112. split_rk(rt, k - 1, t1, t2);
  113. split_rk(t2, 1, t2, t3);
  114. split_rk(t1, k + y - 1, t1, t4);
  115. rt = Merge(Merge(t1, t2), Merge(t4, t3));
  116. }
  117. }
  118. if (op == "Ask") {
  119. cout << Find(Id[x]) - 1 << '\n';
  120. }
  121. if (op == "Query") {
  122. split_rk(rt, x - 1, t1, t2);
  123. split_rk(t2, 1, t2, t3);
  124. cout << t[t2].val << '\n';
  125. rt = Merge(Merge(t1, t2), t3);
  126. }
  127. }
  128. return 0;
  129. }

P3369 【模板】普通平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  1. /*
  2. The code was written by yifan, and yifan is neutral!!!
  3. */
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. typedef long long ll;
  7. #define lc t[u].ls
  8. #define rc t[u].rs
  9. template<typename T>
  10. inline T read() {
  11. T x = 0;
  12. bool fg = 0;
  13. char ch = getchar();
  14. while (ch < '0' || ch > '9') {
  15. fg |= (ch == '-');
  16. ch = getchar();
  17. }
  18. while (ch >= '0' && ch <= '9') {
  19. x = (x << 3) + (x << 1) + (ch ^ 48);
  20. ch = getchar();
  21. }
  22. return fg ? ~x + 1 : x;
  23. }
  24. const int N = 2e5 + 5;
  25. int n, tot, top, rt;
  26. vector<int> rub;
  27. struct node {
  28. int val, pai, siz;
  29. int ls, rs;
  30. } t[N];
  31. inline int newnod(int x) {
  32. int u;
  33. if (!rub.empty()) {
  34. u = rub.back();
  35. rub.pop_back();
  36. }
  37. else {
  38. u = ++ tot;
  39. }
  40. t[u].siz = 1;
  41. t[u].ls = t[u].rs = 0;
  42. t[u].val = x;
  43. t[u].pai = rand();
  44. return u;
  45. }
  46. inline void pushup(int u) {
  47. t[u].siz = t[lc].siz + 1 + t[rc].siz;
  48. }
  49. void split_rk(int u, int k, int &x, int &y) {
  50. if (u == 0) {
  51. x = y = 0;
  52. return ;
  53. }
  54. if (t[lc].siz + 1 <= k) {
  55. x = u;
  56. split_rk(rc, k - t[lc].siz - 1, t[u].rs, y);
  57. }
  58. else {
  59. y = u;
  60. split_rk(lc, k, x, t[u].ls);
  61. }
  62. pushup(u);
  63. }
  64. void split_val(int u, int v, int &x, int &y) {
  65. if (u == 0) {
  66. x = y = 0;
  67. return ;
  68. }
  69. if (t[u].val <= v) {
  70. x = u;
  71. split_val(rc, v, rc, y);
  72. }
  73. else {
  74. y = u;
  75. split_val(lc, v, x, lc);
  76. }
  77. pushup(u);
  78. }
  79. int Merge(int x, int y) {
  80. if (!x || !y) {
  81. return x + y;
  82. }
  83. if (t[x].pai < t[y].pai) {
  84. t[x].rs = Merge(t[x].rs, y);
  85. pushup(x);
  86. return x;
  87. }
  88. else {
  89. t[y].ls = Merge(x, t[y].ls);
  90. pushup(y);
  91. return y;
  92. }
  93. }
  94. inline void Insert(int x) {
  95. int u = newnod(x);
  96. int t1, t2;
  97. split_val(rt, x, t1, t2);
  98. rt = Merge(Merge(t1, u), t2);
  99. }
  100. inline void Erase(int x) {
  101. int t1, t2, t3;
  102. split_val(rt, x - 1, t1, t2);
  103. split_val(t2, x, t2, t3);
  104. rub.emplace_back(t2);
  105. t2 = Merge(t[t2].ls, t[t2].rs);
  106. rt = Merge(Merge(t1, t2), t3);
  107. }
  108. inline int getrank(int x) {
  109. int t1, t2, rk;
  110. split_val(rt, x - 1, t1, t2);
  111. rk = t[t1].siz + 1;
  112. rt = Merge(t1, t2);
  113. return rk;
  114. }
  115. inline int getval(int x) {
  116. int t1, t2, t3, val;
  117. split_rk(rt, x - 1, t1, t2);
  118. split_rk(t2, 1, t2, t3);
  119. val = t[t2].val;
  120. Merge(Merge(t1, t2), t3);
  121. return val;
  122. }
  123. inline int pre(int x) {
  124. int t1, t2, t3, pre;
  125. split_val(rt, x - 1, t1, t2);
  126. split_rk(t1, t[t1].siz - 1, t1, t3);
  127. pre = t[t3].val;
  128. rt = Merge(Merge(t1, t3), t2);
  129. return pre;
  130. }
  131. inline int las(int x) {
  132. int t1, t2, t3, las;
  133. split_val(rt, x, t1, t2);
  134. split_rk(t2, 1, t2, t3);
  135. las = t[t2].val;
  136. rt = Merge(Merge(t1, t2), t3);
  137. return las;
  138. }
  139. int main() {
  140. n = read<int>();
  141. for (int i = 1, x, op; i <= n; ++ i) {
  142. op = read<int>(), x = read<int>();
  143. switch(op) {
  144. case 1 :
  145. Insert(x);
  146. break ;
  147. case 2 :
  148. Erase(x);
  149. break ;
  150. case 3 :
  151. printf("%d\n", getrank(x));
  152. break ;
  153. case 4 :
  154. printf("%d\n", getval(x));
  155. break ;
  156. case 5 :
  157. printf("%d\n", pre(x));
  158. break ;
  159. case 6 :
  160. printf("%d\n", las(x));
  161. break ;
  162. }
  163. }
  164. return 0;
  165. }

「学习笔记」FHQ-treap的更多相关文章

  1. 「学习笔记」 FHQ Treap

    FHQ Treap FHQ Treap (%%%发明者范浩强年年NOI金牌)是一种神奇的数据结构,也叫非旋Treap,它不像Treap zig zag搞不清楚(所以叫非旋嘛),也不像Splay完全看不 ...

  2. 「学习笔记」Treap

    「学习笔记」Treap 前言 什么是 Treap ? 二叉搜索树 (Binary Search Tree/Binary Sort Tree/BST) 基础定义 查找元素 插入元素 删除元素 查找后继 ...

  3. 「学习笔记」平衡树基础:Splay 和 Treap

    「学习笔记」平衡树基础:Splay 和 Treap 点击查看目录 目录 「学习笔记」平衡树基础:Splay 和 Treap 知识点 平衡树概述 Splay 旋转操作 Splay 操作 插入 \(x\) ...

  4. 「学习笔记」Min25筛

    「学习笔记」Min25筛 前言 周指导今天模拟赛五分钟秒第一题,十分钟说第二题是 \(\text{Min25}​\) 筛板子题,要不是第三题出题人数据范围给错了,周指导十五分钟就 \(\text{AK ...

  5. 「学习笔记」FFT 之优化——NTT

    目录 「学习笔记」FFT 之优化--NTT 前言 引入 快速数论变换--NTT 一些引申问题及解决方法 三模数 NTT 拆系数 FFT (MTT) 「学习笔记」FFT 之优化--NTT 前言 \(NT ...

  6. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

  7. 「学习笔记」字符串基础:Hash,KMP与Trie

    「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...

  8. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  9. 「学习笔记」珂朵莉树 ODT

    珂朵莉树,也叫ODT(Old Driver Tree 老司机树) 从前有一天,珂朵莉出现了... 然后有一天,珂朵莉树出现了... 看看图片的地址 Codeforces可还行) 没错,珂朵莉树来自Co ...

  10. 「学习笔记」ST表

    问题引入 先让我们看一个简单的问题,有N个元素,Q次操作,每次操作需要求出一段区间内的最大/小值. 这就是著名的RMQ问题. RMQ问题的解法有很多,如线段树.单调队列(某些情况下).ST表等.这里主 ...

随机推荐

  1. Kubernetes集群调度增强之超容量扩容

    作者:京东科技 徐宪章 1 什么是超容量扩容 超容量扩容功能,是指预先调度一定数量的工作节点,当业务高峰期或者集群整体负载较高时,可以使应用不必等待集群工作节点扩容,从而迅速完成应用横向扩容.通常情况 ...

  2. API Gateway vs Load Balancer:选择适合你的网络流量管理组件

    本文从对比了 API Gateway 和 Load Balancer 的功能区别,帮助读者更好地了解他们在系统架构中扮演的角色. 作者陈泵,API7.ai 技术工程师. 原文链接 由于互联网技术的发展 ...

  3. LLM(大语言模型)解码时是怎么生成文本的?

    Part1配置及参数 transformers==4.28.1 源码地址:transformers/configuration_utils.py at v4.28.1 · huggingface/tr ...

  4. [Pytorch框架] 2.4 卷积神经网络简介

    文章目录 2.4 卷积神经网络简介 2.4.1 为什么要用卷积神经网络 2.4.2结构组成 卷积层 卷积计算 卷积核大小 f 边界填充 (p)adding 步长 (s)tride 计算公式 卷积层 激 ...

  5. 继承 extends

    首先是基础的继承关系,用extend就可以继承. 再者是继承的东西,包括:变量(也包括类变量).全部非私有的属性和方法(除了父类的构造方法) 注:构造方法 class C{ public C() { ...

  6. 2022-06-26:以下golang代码输出什么?A:true;B:false;C:编译错误。 package main import “fmt“ func main() { type

    2022-06-26:以下golang代码输出什么?A:true:B:false:C:编译错误. package main import "fmt" func main() { t ...

  7. 2021-09-25:给定一个字符串数组,将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词指字母相同,但排列不同的字符串。示例 1:输入: strs = [“eat“, “tea“, “

    2021-09-25:给定一个字符串数组,将字母异位词组合在一起.可以按任意顺序返回结果列表.字母异位词指字母相同,但排列不同的字符串.示例 1:输入: strs = ["eat" ...

  8. vue全家桶进阶之路28:项目仓库Gitee

    Gitee(之前称为GitCafe)是一种基于web的Git仓库托管服务,在中国很受欢迎.它为开发人员提供了一个平台,可以托管他们的Git仓库,与其他开发人员协作,并管理他们的代码. https:// ...

  9. 洛谷P3374 【模板】树状数组 1-(单点修改,区间查询)

    题目描述 如题,已知一个数列,你需要进行下面两种操作: 将某一个数加上 x 求出某区间每一个数的和 输入格式 第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数. 第二行包含 n 个 ...

  10. windows下搭建docker容器环境

    下载Docker Desktop https://www.docker.com/ 安装Docker Desktop(软件默认安装c盘,若要安装到其他盘,在安装之前创建软连接再进行安装) 在自定义磁盘中 ...