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

以点权作为排序依据

struct Splay{
#define ls p[u].son[0]
#define rs p[u].son[1]
#define maxn 100000 int root, cnt;
struct Node{
int val, fa, size, sum;
int son[2];
}p[maxn]; inline void destroy(int u){
p[u].val = ls = rs = p[u].fa = p[u].sum = p[u].size = 0;
} inline int identify(int u){
return p[p[u].fa].son[1] == u;
} inline void update(int u){
if(u) p[u].sum = p[ls].sum + p[rs].sum + p[u].size;
} void rotate(int u){
int f = p[u].fa, gf = p[f].fa, sta = identify(u), sta_f = identify(f);
p[f].son[sta] = p[u].son[sta ^ 1];
p[p[f].son[sta]].fa = f;
p[u].son[sta^1] = f, p[f].fa = u, p[u].fa = gf;
p[gf].son[sta_f] = u;
update(f);
} void splay(int u, int goal){
for(int f; (f = p[u].fa) && (f != goal); rotate(u))
if(p[f].fa != goal) rotate(identify(u) == identify(f) ? f : u);
if(!goal) root = u;
update(u);
} void insert(int u){ // 函数结束后权值为u的节点变为根节点
if(!root){
p[++cnt].val = u;
p[cnt].size = p[cnt].sum = 1;
root = cnt;
return ;
}
int now = root, f = 0;
while(true){
if(u == p[now].val){
++p[now].size;
splay(now, 0);
return ;
}
f = now, now = p[now].son[p[now].val < u];
if(!now){
p[++cnt].val = u;
p[cnt].size = p[cnt].sum = 1;
p[cnt].fa = f, p[f].son[p[f].val < u] = cnt;
++p[f].sum;
splay(cnt, 0);
return ;
}
}
} int find_val(int rank){
int now = root;
while(true){
if(p[now].son[0] && rank <= p[p[now].son[0]].sum)
now = p[now].son[0];
else{
int temp = p[p[now].son[0]].sum + p[now].size;
if(rank <= temp) return p[now].val;
now = p[now].son[1], rank -= temp;
}
}
} int find_rank(int u){ // 函数结束后权值为u的节点是根节点
int now = root, rank = 0;
while(true){
if(u < p[now].val) now = p[now].son[0];
else{
rank += p[p[now].son[0]].sum;
if(u == p[now].val){
splay(now, 0);
return rank + 1;
}
rank += p[now].size, now = p[now].son[1];
}
}
} int find_pre(int x){ // 返回x前驱节点编号
insert(x);
int now = p[root].son[0];
while(p[now].son[1]) now = p[now].son[1];
delete_val(x);
return now;
} int find_suffix(int x){ // 返回x后继节点编号
insert(x);
int now = p[root].son[1];
while(p[now].son[0]) now = p[now].son[0];
delete_val(x);
return now;
} void delete_val(int u){
find_rank(u); // 将权值为u的节点旋转到根节点
if(p[root].size > 1){
--p[root].size, --p[root].sum;
return ;
}
if(!p[root].son[0] && !p[root].son[1]){
destroy(root), root = 0;
return ;
}
int old_root = root;
if(!p[root].son[0]){
root = p[root].son[1];
p[root].fa = 0;
destroy(old_root);
return ;
}
if(!p[root].son[1]){
root = p[root].son[0];
p[root].fa = 0;
destroy(old_root);
return ;
}
int left_max = find_pre(u);
splay(left_max, 0);
p[root].son[1] = p[old_root].son[1];
p[p[old_root].son[1]].fa = root;
destroy(old_root);
update(root);
}
}splay;

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

struct Splay{
#define ls p[u].son[0]
#define rs p[u].son[1]
#define maxn N
static const int inf = 1e9; // 因为有虚点直接初始化了
int root = 1, top = 0, temp = 5e5 + 45;
int id[N], c[N], cnt[N]; struct Node{
int fa, size, len, reset, sum; // size是子树大小,len是除去虚点的子树大小
int tag, val, l_max, r_max, mid_max;
int son[2];
Node() { reset = inf; }
}p[maxn]; inline int identify(int u){
return p[p[u].fa].son[1] == u;
} // 空间回收
void destroy(int u){
if(!u) return ;
if(ls) destroy(ls);
if(rs) destroy(rs);
p[u] = p[temp];
id[++top] = u;
} inline void update(int u){
p[u].size = p[ls].size + p[rs].size + 1;
p[u].len = p[ls].len + p[rs].len + (u > 2); // 判断u > 2是为了除去虚点的影响
p[u].sum = p[ls].sum + p[rs].sum + p[u].val;
p[u].l_max = max(p[ls].l_max, p[ls].sum + p[u].val + p[rs].l_max);
p[u].r_max = max(p[rs].r_max, p[rs].sum + p[u].val + p[ls].r_max);
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));
} void change(int u, int val){
p[u].val = p[u].reset = val;
p[u].sum = p[u].val * p[u].len;
p[u].l_max = p[u].r_max = max(0, p[u].sum);
p[u].mid_max = max(val, p[u].sum);
} inline void pushdown(int u){
if(p[u].reset != inf){
if(ls) change(ls, p[u].reset);
if(rs) change(rs, p[u].reset);
p[u].reset = inf, p[u].tag = 0;
}
if(p[u].tag){
if(ls) p[ls].tag ^= 1, swap(p[ls].son[0], p[ls].son[1]), swap(p[ls].l_max, p[ls].r_max);
if(rs) p[rs].tag ^= 1, swap(p[rs].son[0], p[rs].son[1]), swap(p[rs].l_max, p[rs].r_max);
p[u].tag = 0;
}
} void rotate(int u){
int f = p[u].fa, gf = p[f].fa, sta = identify(u), sta_f = identify(f);
p[f].son[sta] = p[u].son[sta ^ 1];
p[p[f].son[sta]].fa = f;
p[u].son[sta^1] = f, p[f].fa = u, p[u].fa = gf;
p[gf].son[sta_f] = u;
update(f);
} void splay(int u, int goal){
for(int f; (f = p[u].fa) && (f != goal); rotate(u)){
if(p[f].fa != goal) rotate(identify(u) == identify(f) ? f : u);
}
if(!goal) root = u;
update(u);
} int find_Kth(int k){
int u = root;
while(1){
pushdown(u);
if(p[ls].size + 1 == k) return u;
if(p[ls].size >= k) u = ls;
else k -= p[ls].size + 1, u = rs;
}
} int build(int l, int r, int fa){
if(l > r) return 0;
int mid = (l + r) >> 1, now = cnt[mid];
if(l == r){
p[now].val = c[l];
p[now].fa = fa;
update(now);
p[now].mid_max = p[now].val;
return now;
}
p[now].fa = fa, p[now].val = c[mid];
p[now].son[0] = build(l, mid - 1, now);
p[now].son[1] = build(mid + 1, r, now);
update(now);
return now;
} // 在第u个位置后面插入值插入tot个数
void insert(int u, int tot){
for(int i = 1; i <= tot; ++i) scanf("%d", &c[i]), cnt[i] = id[top--];
int rt = build(1, tot, 0);
int L = find_Kth(u), R = find_Kth(u + 1);
splay(L, 0), splay(R, L);
p[rt].fa = R, p[R].son[0] = rt;
splay(rt, 0);
} // 区间删除
void delete_range(int pos, int tot){
int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
splay(L, 0), splay(R, L);
destroy(p[R].son[0]);
p[R].son[0] = 0;
update(R), update(L);
} // 区间修改
void modify_range(int pos, int tot, int set){
int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
splay(L, 0), splay(R, L);
int u = p[R].son[0];
p[u].reset = p[u].val = set;
p[u].sum = p[u].len * set;
p[u].l_max = p[u].r_max = max(0, p[u].sum);
p[u].mid_max = max(p[u].sum, set);
update(R), update(L);
} // 翻转区间
void reverse(int pos, int tot){
int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
splay(L, 0), splay(R, L);
int u = p[R].son[0];
p[u].tag ^= 1;
swap(ls, rs);
swap(p[u].l_max, p[u].r_max);
update(R), update(L);
} int get_sum(int pos, int tot){
int L = find_Kth(pos), R = find_Kth(pos + tot + 1);
splay(L, 0), splay(R, L);
return p[p[R].son[0]].sum;
} // 非空子段最大值
int Max(){
return p[root].mid_max;
} // 插入虚点,之后操作要注意下标
void init(){
for(int i = 3; i <= N - 45; ++i) id[++top] = i;
p[1].son[1] = 2, p[2].fa = 1;
p[1].size = 2, p[2].size = 1;
// 虚点赋值负无穷,消除影响,根据不同题而定
p[0].mid_max = p[1].mid_max = p[2].mid_max = p[0].val = p[1].val = p[2].val = -inf;
}
}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. 【必看!】阿里云推出QWen-7B和QWen-7b-Chat,开放免费商用!

    阿里云于8月3日宣布开源两款重要的大型模型--QWen-7B和QWen-7b-Chat.这两款模型的参数规模达到了令人瞩目的70亿,并且已经在Hugging Face和ModelScope平台上开放, ...

  2. JavaScript 基础(1) - 笔记

    1 JavaScript基础 1.1 JavaScript 是什么 1.JavaScript(是什么?) 是一种运行在客户端(浏览器)的编程语言,实现人机交互效果. 2.作用(做什么?) 网页特效(监 ...

  3. 推荐几款三维模型OBJ格式轻量化处理工具软件

    推荐几款三维模型OBJ格式轻量化处理工具软件 以下是几款常用的三维模型OBJ格式轻量化处理软件的介绍: 1.MeshLab: MeshLab是一款免费且强大的开源三维模型处理软件,支持多种文件格式,包 ...

  4. langchain中的LLM模型使用介绍

    简介 构建在大语言模型基础上的应用通常有两种,第一种叫做text completion,也就是一问一答的模式,输入是text,输出也是text.这种模型下应用并不会记忆之前的问题内容,每一个问题都是最 ...

  5. C++算法之旅、06 基础篇 | 第四章 动态规划 详解

    常见问题 闫式DP分析法 状态表示 集合 满足一定条件的所有方案 属性 集合(所有方案)的某种属性(Max.Min.Count等) 状态计算(集合划分) 如何将当前集合划分成多个子集合 状态计算相当于 ...

  6. ES13 中11个令人惊叹的 JavaScript 新特性

    前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...

  7. 【krpano】KRPano测试开发专用浏览器

    KRPano技术解密网站:http://www.krpano.tech 在开发KRPano项目时候,每次测试时均需要打开测试服务器,或者上传到空间中才能进行测试,十分不方便. 因此本站提供了免费的KR ...

  8. 自定义注解实现数据序列化时进行数据脱敏(基于springboot默认jackjson)、消息转换器HttpMessageConverter

    消息转换器 HttpMessageConverter 消息转化器的作用 将请求报文转化为Java对象 将Java对象转化为响应报文 消息转换器接口 public interface HttpMessa ...

  9. 主动写入流对@ResponseBody注解的影响

    问题回溯 2023年Q2某日运营反馈一个问题,商品系统商家中心某批量工具模板无法下载,导致功能无法使用(因为模板是动态变化的) 商家中心报错(JSON串): {"code":-1, ...

  10. Asp-Net-Core开发笔记:EFCore统一实体和属性命名风格

    前言 C# 编码规范中,类和属性都是大写驼峰命名风格(PascalCase / UpperCamelCase),而在数据库中我们往往使用小写蛇形命名(snake_case),在默认情况下,EFCore ...