注意确保操作合法性,否则可能陷入死循环

以点权作为排序依据

  1. struct Splay{
  2. #define ls p[u].son[0]
  3. #define rs p[u].son[1]
  4. #define maxn 100000
  5. int root, cnt;
  6. struct Node{
  7. int val, fa, size, sum;
  8. int son[2];
  9. }p[maxn];
  10. inline void destroy(int u){
  11. p[u].val = ls = rs = p[u].fa = p[u].sum = p[u].size = 0;
  12. }
  13. inline int identify(int u){
  14. return p[p[u].fa].son[1] == u;
  15. }
  16. inline void update(int u){
  17. if(u) p[u].sum = p[ls].sum + p[rs].sum + p[u].size;
  18. }
  19. void rotate(int u){
  20. int f = p[u].fa, gf = p[f].fa, sta = identify(u), sta_f = identify(f);
  21. p[f].son[sta] = p[u].son[sta ^ 1];
  22. p[p[f].son[sta]].fa = f;
  23. p[u].son[sta^1] = f, p[f].fa = u, p[u].fa = gf;
  24. p[gf].son[sta_f] = u;
  25. update(f);
  26. }
  27. void splay(int u, int goal){
  28. for(int f; (f = p[u].fa) && (f != goal); rotate(u))
  29. if(p[f].fa != goal) rotate(identify(u) == identify(f) ? f : u);
  30. if(!goal) root = u;
  31. update(u);
  32. }
  33. void insert(int u){ // 函数结束后权值为u的节点变为根节点
  34. if(!root){
  35. p[++cnt].val = u;
  36. p[cnt].size = p[cnt].sum = 1;
  37. root = cnt;
  38. return ;
  39. }
  40. int now = root, f = 0;
  41. while(true){
  42. if(u == p[now].val){
  43. ++p[now].size;
  44. splay(now, 0);
  45. return ;
  46. }
  47. f = now, now = p[now].son[p[now].val < u];
  48. if(!now){
  49. p[++cnt].val = u;
  50. p[cnt].size = p[cnt].sum = 1;
  51. p[cnt].fa = f, p[f].son[p[f].val < u] = cnt;
  52. ++p[f].sum;
  53. splay(cnt, 0);
  54. return ;
  55. }
  56. }
  57. }
  58. int find_val(int rank){
  59. int now = root;
  60. while(true){
  61. if(p[now].son[0] && rank <= p[p[now].son[0]].sum)
  62. now = p[now].son[0];
  63. else{
  64. int temp = p[p[now].son[0]].sum + p[now].size;
  65. if(rank <= temp) return p[now].val;
  66. now = p[now].son[1], rank -= temp;
  67. }
  68. }
  69. }
  70. int find_rank(int u){ // 函数结束后权值为u的节点是根节点
  71. int now = root, rank = 0;
  72. while(true){
  73. if(u < p[now].val) now = p[now].son[0];
  74. else{
  75. rank += p[p[now].son[0]].sum;
  76. if(u == p[now].val){
  77. splay(now, 0);
  78. return rank + 1;
  79. }
  80. rank += p[now].size, now = p[now].son[1];
  81. }
  82. }
  83. }
  84. int find_pre(int x){ // 返回x前驱节点编号
  85. insert(x);
  86. int now = p[root].son[0];
  87. while(p[now].son[1]) now = p[now].son[1];
  88. delete_val(x);
  89. return now;
  90. }
  91. int find_suffix(int x){ // 返回x后继节点编号
  92. insert(x);
  93. int now = p[root].son[1];
  94. while(p[now].son[0]) now = p[now].son[0];
  95. delete_val(x);
  96. return now;
  97. }
  98. void delete_val(int u){
  99. find_rank(u); // 将权值为u的节点旋转到根节点
  100. if(p[root].size > 1){
  101. --p[root].size, --p[root].sum;
  102. return ;
  103. }
  104. if(!p[root].son[0] && !p[root].son[1]){
  105. destroy(root), root = 0;
  106. return ;
  107. }
  108. int old_root = root;
  109. if(!p[root].son[0]){
  110. root = p[root].son[1];
  111. p[root].fa = 0;
  112. destroy(old_root);
  113. return ;
  114. }
  115. if(!p[root].son[1]){
  116. root = p[root].son[0];
  117. p[root].fa = 0;
  118. destroy(old_root);
  119. return ;
  120. }
  121. int left_max = find_pre(u);
  122. splay(left_max, 0);
  123. p[root].son[1] = p[old_root].son[1];
  124. p[p[old_root].son[1]].fa = root;
  125. destroy(old_root);
  126. update(root);
  127. }
  128. }splay;

以序列下标作为排序依据,常用于需要增删序列的操作(区间平衡树)

  1. struct Splay{
  2. #define ls p[u].son[0]
  3. #define rs p[u].son[1]
  4. #define maxn N
  5. static const int inf = 1e9;
  6. // 因为有虚点直接初始化了
  7. int root = 1, top = 0, temp = 5e5 + 45;
  8. int id[N], c[N], cnt[N];
  9. struct Node{
  10. int fa, size, len, reset, sum; // size是子树大小,len是除去虚点的子树大小
  11. int tag, val, l_max, r_max, mid_max;
  12. int son[2];
  13. Node() { reset = inf; }
  14. }p[maxn];
  15. inline int identify(int u){
  16. return p[p[u].fa].son[1] == u;
  17. }
  18. // 空间回收
  19. void destroy(int u){
  20. if(!u) return ;
  21. if(ls) destroy(ls);
  22. if(rs) destroy(rs);
  23. p[u] = p[temp];
  24. id[++top] = u;
  25. }
  26. inline void update(int u){
  27. p[u].size = p[ls].size + p[rs].size + 1;
  28. p[u].len = p[ls].len + p[rs].len + (u > 2); // 判断u > 2是为了除去虚点的影响
  29. p[u].sum = p[ls].sum + p[rs].sum + p[u].val;
  30. p[u].l_max = max(p[ls].l_max, p[ls].sum + p[u].val + p[rs].l_max);
  31. p[u].r_max = max(p[rs].r_max, p[rs].sum + p[u].val + p[ls].r_max);
  32. p[u].mid_max = max(p[u].val + p[ls].r_max + p[rs].l_max, max(p[ls].mid_max, p[rs].mid_max));
  33. }
  34. void change(int u, int val){
  35. p[u].val = p[u].reset = val;
  36. p[u].sum = p[u].val * p[u].len;
  37. p[u].l_max = p[u].r_max = max(0, p[u].sum);
  38. p[u].mid_max = max(val, p[u].sum);
  39. }
  40. inline void pushdown(int u){
  41. if(p[u].reset != inf){
  42. if(ls) change(ls, p[u].reset);
  43. if(rs) change(rs, p[u].reset);
  44. p[u].reset = inf, p[u].tag = 0;
  45. }
  46. if(p[u].tag){
  47. if(ls) p[ls].tag ^= 1, swap(p[ls].son[0], p[ls].son[1]), swap(p[ls].l_max, p[ls].r_max);
  48. if(rs) p[rs].tag ^= 1, swap(p[rs].son[0], p[rs].son[1]), swap(p[rs].l_max, p[rs].r_max);
  49. p[u].tag = 0;
  50. }
  51. }
  52. void rotate(int u){
  53. int f = p[u].fa, gf = p[f].fa, sta = identify(u), sta_f = identify(f);
  54. p[f].son[sta] = p[u].son[sta ^ 1];
  55. p[p[f].son[sta]].fa = f;
  56. p[u].son[sta^1] = f, p[f].fa = u, p[u].fa = gf;
  57. p[gf].son[sta_f] = u;
  58. update(f);
  59. }
  60. void splay(int u, int goal){
  61. for(int f; (f = p[u].fa) && (f != goal); rotate(u)){
  62. if(p[f].fa != goal) rotate(identify(u) == identify(f) ? f : u);
  63. }
  64. if(!goal) root = u;
  65. update(u);
  66. }
  67. int find_Kth(int k){
  68. int u = root;
  69. while(1){
  70. pushdown(u);
  71. if(p[ls].size + 1 == k) return u;
  72. if(p[ls].size >= k) u = ls;
  73. else k -= p[ls].size + 1, u = rs;
  74. }
  75. }
  76. int build(int l, int r, int fa){
  77. if(l > r) return 0;
  78. int mid = (l + r) >> 1, now = cnt[mid];
  79. if(l == r){
  80. p[now].val = c[l];
  81. p[now].fa = fa;
  82. update(now);
  83. p[now].mid_max = p[now].val;
  84. return now;
  85. }
  86. p[now].fa = fa, p[now].val = c[mid];
  87. p[now].son[0] = build(l, mid - 1, now);
  88. p[now].son[1] = build(mid + 1, r, now);
  89. update(now);
  90. return now;
  91. }
  92. // 在第u个位置后面插入值插入tot个数
  93. void insert(int u, int tot){
  94. for(int i = 1; i <= tot; ++i) scanf("%d", &c[i]), cnt[i] = id[top--];
  95. int rt = build(1, tot, 0);
  96. int L = find_Kth(u), R = find_Kth(u + 1);
  97. splay(L, 0), splay(R, L);
  98. p[rt].fa = R, p[R].son[0] = rt;
  99. splay(rt, 0);
  100. }
  101. // 区间删除
  102. void delete_range(int pos, int tot){
  103. int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
  104. splay(L, 0), splay(R, L);
  105. destroy(p[R].son[0]);
  106. p[R].son[0] = 0;
  107. update(R), update(L);
  108. }
  109. // 区间修改
  110. void modify_range(int pos, int tot, int set){
  111. int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
  112. splay(L, 0), splay(R, L);
  113. int u = p[R].son[0];
  114. p[u].reset = p[u].val = set;
  115. p[u].sum = p[u].len * set;
  116. p[u].l_max = p[u].r_max = max(0, p[u].sum);
  117. p[u].mid_max = max(p[u].sum, set);
  118. update(R), update(L);
  119. }
  120. // 翻转区间
  121. void reverse(int pos, int tot){
  122. int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
  123. splay(L, 0), splay(R, L);
  124. int u = p[R].son[0];
  125. p[u].tag ^= 1;
  126. swap(ls, rs);
  127. swap(p[u].l_max, p[u].r_max);
  128. update(R), update(L);
  129. }
  130. int get_sum(int pos, int tot){
  131. int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
  132. splay(L, 0), splay(R, L);
  133. return p[p[R].son[0]].sum;
  134. }
  135. // 非空子段最大值
  136. int Max(){
  137. return p[root].mid_max;
  138. }
  139. // 插入虚点,之后操作要注意下标
  140. void init(){
  141. for(int i = 3; i <= N - 45; ++i) id[++top] = i;
  142. p[1].son[1] = 2, p[2].fa = 1;
  143. p[1].size = 2, p[2].size = 1;
  144. // 虚点赋值负无穷,消除影响,根据不同题而定
  145. p[0].mid_max = p[1].mid_max = p[2].mid_max = p[0].val = p[1].val = p[2].val = -inf;
  146. }
  147. }splay;

【封装】Splay的更多相关文章

  1. [学习笔记] Splay Tree 从入门到放弃

    前几天由于出行计划没有更博QwQ (其实是因为调试死活调不出来了TAT我好菜啊) 伸展树 伸展树(英语:Splay Tree)是一种二叉查找树,它能在O(log n)内完成插入.查找和删除操作.它是由 ...

  2. BZOJ 3196 Tyvj 1730 二逼平衡树:线段树套splay

    传送门 题意 给你一个长度为 $ n $ 有序数列 $ a $ ,进行 $ m $ 次操作,操作有如下几种: 查询 $ k $ 在区间 $ [l,r] $ 内的排名 查询区间 $ [l,r] $ 内排 ...

  3. AVL树、splay树(伸展树)和红黑树比较

    AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...

  4. [C#] 简单的 Helper 封装 -- RegularExpressionHelper

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. iOS开发之App间账号共享与SDK封装

    上篇博客<iOS逆向工程之KeyChain与Snoop-it>中已经提到了,App间的数据共享可以使用KeyChian来实现.本篇博客就实战一下呢.开门见山,本篇博客会封装一个登录用的SD ...

  6. Ajax实现原理,代码封装

    都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...

  7. 用C语言封装OC对象(耐心阅读,非常重要)

    用C语言封装OC对象(耐心阅读,非常重要) 本文的主要内容来自这里 前言 做iOS开发的朋友,对OC肯定非常了解,那么大家有没有想过OC中NSInteger,NSObject,NSString这些对象 ...

  8. 【知识必备】RxJava+Retrofit二次封装最佳结合体验,打造懒人封装框架~

    一.写在前面 相信各位看官对retrofit和rxjava已经耳熟能详了,最近一直在学习retrofit+rxjava的各种封装姿势,也结合自己的理解,一步一步的做起来. 骚年,如果你还没有掌握ret ...

  9. 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)

    前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...

  10. 封装集合(Encapsulate Collection)

    封装就是将相关的方法或者属性抽象成为一个对象. 封装的意义: 对外隐藏内部实现,接口不变,内部实现自由修改. 只返回需要的数据和方法. 提供一种方式防止数据被修改. 更好的代码复用. 当一个类的属性类 ...

随机推荐

  1. VMware中的三种网络模式

    1.桥接模式网络 通过桥接模式网络连接,虚拟机中的虚拟网络适配器可连接到主机中的物理网络适配器.虚拟机可通过主机网络适配器连接到主机系统所用的 LAN.桥接模式网络连接支持有线和无线主机网络适配器. ...

  2. 文盘Rust -- Mutex解决并发写文件乱序问题

    在实际开发过程中,我们可能会遇到并发写文件的场景,如果处理不当很可能出现文件内容乱序问题.下面我们通过一个示例程序描述这一过程并给出解决该问题的方法. use std::{ fs::{self, Fi ...

  3. 基于C#的无边框窗体阴影绘制方案 - 开源研究系列文章

    今天介绍无边框窗体阴影绘制的内容. 上次有介绍使用双窗体的方法来显示阴影,这次介绍使用API函数来进行绘制.这里使用的是Windows API函数,操作系统的窗体也是用的这个来进行的绘制. 1. 项目 ...

  4. CodeForces 1332D Walk on Matrix

    题意 \(Bob\)想解决一个问题:一个\(n\cdot m\)的矩阵,从\((1,1)\)出发,只能走右和下,问从\((1,1)\)到\((n,m)\)的最大\(\&\)和 他的算法如下(\ ...

  5. [TSG开发日志](一)软件基础框架

    目录 前言 说明 框架 TSG_Framework 一.底层信号机制 TSG_Caller 二.参数类型声明 TSG_Params 三.设备类声明 TSG_Device 四.设备配置文件控制 TSG_ ...

  6. DesignPattern-part3

    title: "modern C++ DesignPattern-Part3" date: 2018-04-12T19:08:49+08:00 lastmod: 2018-04-1 ...

  7. 扩展ABP的Webhook功能,推送数据到第三方接口(企业微信群、钉钉群等)

    前言 在上一篇文章[基于ASP.NET ZERO,开发SaaS版供应链管理系统]中有提到对Webhook功能的扩展改造,本文详细介绍一下具体过程. Webhook功能操作说明,请参见此文档链接:Web ...

  8. 开源.NetCore通用工具库Xmtool使用连载 - 图形验证码篇

    [Github源码] <上一篇> 介绍了Xmtool工具库中的Web操作类库,今天我们继续为大家介绍其中的图形验证码类库. 图形验证码是为了抵御恶意攻击出现的一种设计:例如用户登录.修改密 ...

  9. 「hdu - 5780」gcd

    link. 钦定 \(i>j\),研究得 \((x^i-1,x^j-1)\rightleftharpoons(x^i-x^j,x^j-1)\rightleftharpoons(x^j(x^{i- ...

  10. Cplex求解教程(基于OPL语言,可作为大规模运算输入参考)

    最近导导让牛牛改篇论文,牛牛在她的指导下把非线性问题化成了线性.然鹅,化成线性后的模型决策变量和约束条件均达到上百甚至上千个,这让牛牛犯了难,以下方法或许能为这样大规模模型的变量和约束输入提供思路(๑ ...