当前位置: 首页 > news >正文

详细介绍:进阶数据结构Splay应用-维护数列

引言

该问题几乎包含了所有的splaysplaysplay操作, 假设不了解splaysplaysplay可以单击这里

题目-维护数列

问题分析

因为涉及到区间翻转操作, 线段树无法实现(线段树解决的是区间属性问题)

其实最复杂的操作是求区间的最大
子段和
, 可以考虑splaysplaysplay维护的节点信息

splaysplaysplay每个节点表示一个数字, 多个点构成的的中序遍历就是希望求的序列, 因此序列就是中序遍历维护的

算法步骤

splaysplaysplay维护的节点信息

  • 维护最大子段和,maxv,lm,rm,summaxv, lm, rm, summaxv,lm,rm,sum
  • 节点儿子, 父节点信息s,ps, ps,p, 当前节点的权值vvv
  • 是否翻转revrevrev
  • 查询节点的前驱和后继cntcntcnt
  • 整个区间是否变成相同的数samesamesame

实现细节

如果一个点有延迟标记, 这个点的权值存储的是执行延迟标记之前的值还是执行延迟标记之后的值需要自己去定义

因为执行samesamesame或者revrevrev延迟标记, 会对当前节点的值(maxv,lm,rm,summaxv, lm, rm, summaxv,lm,rm,sum)产生影响, 能够定义当前节点的值(maxv,lm,rm,summaxv, lm, rm, summaxv,lm,rm,sum)是延迟标记操作之后的值

具体的来说

对于当前节点, 如果打上了延迟标记, 那么将该节点的maxv,lm,rm,summaxv, lm, rm, summaxv,lm,rm,sum都计算一遍


并且在节点执行push down延迟操作的时候, 不仅仅是直接将子节点打上标记, 而且将子节点的maxv,lm,rm,summaxv, lm, rm, summaxv,lm,rm,sum计算一遍

在删除过程中需要进行内存回收优化内存将删除的节点就是, 也就回收到节点池

因此初始化的时候, ms = sum = v, 不能是max(v, 0)

代码实现

注意事项:

  • 计算跨区间的情况, 需要加当前节点的值vvv
  • 注意内存池的指针变量不要和父节点变量ppp重名
  • 因为有哨兵节点的存在, 注意get_k()的参数
  • 延迟标记的处理, 规定若是当前对某个子树打上延迟标记, 对于当前节点立刻生效
  • 题目要求最大子段和最少囊括一个点, 因此初始化ms的时候, 不能设置为max⁡(v,0)\max(v, 0)max(v,0), 而是没有选择的设置为vvv
  • 000号点是边界哨兵节点, 因此初始化节点池的时候, 必须从111开始
#include <bits/stdc++.h>using namespace std;const int N = 5e5 + 10, INF = 1e9;int n, m;struct Node {int s[2], p, v;int rev, same, cnt;int ms, ls, rs, sum;// 因为内存回收机制的存在, 必须在此刻重置rev和same的延迟标记void init(int _v, int _p) {s[0] = s[1] = 0;p = _p, v = _v;cnt = 1;rev = same = 0;sum = ms = v;ls = rs = max(v, 0);}} tr[N];// nodes作为节点池, ptr分配节点int root, nodes[N], ptr;int w[N];void pushup(int u) {Node &lson = tr[tr[u].s[0]], &rson = tr[tr[u].s[1]];tr[u].cnt = lson.cnt + rson.cnt + 1;tr[u].sum = lson.sum + rson.sum + tr[u].v;tr[u].ls = max(lson.ls, lson.sum + tr[u].v + rson.ls);tr[u].rs = max(rson.rs, rson.sum + tr[u].v + lson.rs);// 注意需要加当前节点的值tr[u].ms = max({lson.ms, rson.ms, lson.rs + tr[u].v + rson.ls});}void pushdown(int u) {Node &lson = tr[tr[u].s[0]], &rson = tr[tr[u].s[1]];if (tr[u].same) {tr[u].same = tr[u].rev = 0;if (tr[u].s[0]) {lson.same = 1;lson.v = tr[u].v;lson.sum = tr[u].v * lson.cnt;if (tr[u].v > 0) lson.ms = lson.ls = lson.rs = lson.sum;else {lson.ms = tr[u].v;lson.ls = lson.rs = 0;}}if (tr[u].s[1]) {rson.same = 1;rson.v = tr[u].v;rson.sum = tr[u].v * rson.cnt;if (tr[u].v > 0) rson.ms = rson.ls = rson.rs = rson.sum;else {rson.ms = tr[u].v;rson.ls = rson.rs = 0;}}}// 当子树的根被打上标记, 约定翻转, 这里就不需要翻转else if (tr[u].rev) {tr[u].rev = 0;lson.rev ^= 1, rson.rev ^= 1;swap(lson.ls, lson.rs), swap(rson.ls, rson.rs);swap(lson.s[0], lson.s[1]), swap(rson.s[0], rson.s[1]);}}void rotate(int x) {int y = tr[x].p, z = tr[y].p;int k = tr[y].s[1] == x;tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;tr[x].s[k ^ 1] = y, tr[y].p = x;pushup(y), pushup(x);}void splay(int x, int k) {while (tr[x].p != k) {int y = tr[x].p, z = tr[y].p;if (z != k) {if ((tr[z].s[1] == y) ^ (tr[y].s[1] == x)) rotate(x);else rotate(y);}rotate(x);}if (!k) root = x;}// 将一段序列构建二叉树int build(int l, int r, int p) {int mid = l + r >> 1;int u = nodes[ptr--];tr[u].init(w[mid], p);if (l < mid) tr[u].s[0] = build(l, mid - 1, u);if (r > mid) tr[u].s[1] = build(mid + 1, r, u);pushup(u);return u;}int get_k(int k) {int u = root;while (u) {pushdown(u);int cnt = tr[tr[u].s[0]].cnt + 1;if (cnt > k) u = tr[u].s[0];else if (cnt == k) return u;else u = tr[u].s[1], k -= cnt;}return 0;}// 内存回收void release(int u) {if (tr[u].s[0]) release(tr[u].s[0]);if (tr[u].s[1]) release(tr[u].s[1]);nodes[++ptr] = u;}int main() {ios::sync_with_stdio(false);cin.tie(0);// 所有点是可用的for (int i = 1; i < N; ++i) nodes[++ptr] = i;cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> w[i];// 为了保证数列时刻都有数字以及如果当前节点没有左右儿子节点不会造成影响tr[0].ms = w[0] = w[n + 1] = -INF;// 原序列初始化为树root = build(0, n + 1, 0);string op;while (m--) {cin >> op;if (op == "INSERT") {int posi, tot;cin >> posi >> tot;int u = get_k(posi + 1), v = get_k(posi + 2);splay(u, 0), splay(v, u);for (int i = 0; i < tot; ++i) cin >> w[i];tr[v].s[0] = build(0, tot - 1, v);pushup(v), pushup(u);}else if (op == "DELETE") {int posi, tot;cin >> posi >> tot;int u = get_k(posi), v = get_k(tot + posi + 1);splay(u, 0), splay(v, u);release(tr[v].s[0]);tr[v].s[0] = 0;pushup(v), pushup(u);}else if (op == "MAKE-SAME") {int posi, tot, c;cin >> posi >> tot >> c;int u = get_k(posi), v = get_k(tot + posi + 1);splay(u, 0), splay(v, u);Node &son = tr[tr[v].s[0]];son.same = 1;son.v = c;son.sum = son.cnt * c;if (c > 0) son.ms = son.ls = son.rs = son.sum;else {son.ms = c;son.ls = son.rs = 0;}pushup(v), pushup(u);}else if (op == "REVERSE") {int posi, tot;cin >> posi >> tot;int u = get_k(posi), v = get_k(tot + posi + 1);splay(u, 0), splay(v, u);Node &son = tr[tr[v].s[0]];son.rev ^= 1;swap(son.ls, son.rs);swap(son.s[0], son.s[1]);pushup(v), pushup(u);}else if (op == "GET-SUM") {int posi, tot;cin >> posi >> tot;int u = get_k(posi), v = get_k(tot + posi + 1);splay(u, 0), splay(v, u);cout << tr[tr[v].s[0]].sum << '\n';pushup(v), pushup(u);}else {cout << tr[root].ms << '\n';}}return 0;}
http://www.jsqmd.com/news/272847/

相关文章:

  • Python 流程控制详解:条件语句 + 循环语句 + 人生重开模拟器实战
  • springboot高校学习讲座预约管理系统设计实现
  • hive 小文件优化
  • Java核心语法:从变量到流程控制
  • springboot攻防靶场实验室平台的设计与实现
  • 如何轻松将 Python 英文版切换至中文界面
  • 元宇宙:数字文明的下一站
  • 物联网 (IoT) 助力您提升业务的 9 种方式
  • Delphi 与 VS 调试快捷键精准对应表
  • 硅基计划4.0 算法 递归回溯 - 实践
  • 如何为制造业选geo优化公司?2026年geo优化公司全面评测与推荐,直击精准询盘痛点 - 品牌推荐
  • 钱包技术:从私钥保管到Web3入口的演进之路
  • EI会议推荐!2026年机器视觉、检测与三维成像技术国际学术会议(MVDIT 2026)
  • 数据安全有保障的BI产品?观远数据筑牢企业核心资产防护墙 - 速递信息
  • 单北斗GNSS变形监测系统是什么?主要应用于水库和桥梁形变监测吗?
  • 操作系统进程间通信(IPC)的庖丁解牛
  • springboot高等数学课程教辅资源系统的设计与实现
  • 2026年GEO优化公司推荐:针对知识密集型行业痛点排名,涵盖法律与教育多场景应用 - 品牌推荐
  • EI往届检索稳定JPCS出版| 往届检索可查 | 第四届机械工程与先进制造智能化技术研讨会(MEAMIT 2026)
  • springboot高校党员信息管理系统
  • 好写作AI|回复“刁钻”审稿意见的智囊:当AI开始“阅读理解”审稿人的潜台词…
  • 命名管道和匿名管道
  • springboot高校督导听查课支持服务系统
  • 2026年知名的数控凸轮磨床生产商哪家靠谱?口碑排行 - 品牌宣传支持者
  • 当银行被迫为“被骗”买单:韩国拟推语音钓鱼强制赔偿制,引发金融安全与道德风险大辩论
  • 知名的服装衬布公司哪家靠谱?2026年行业口碑排行 - 品牌宣传支持者
  • Prodigy AI标注工具v1.18更新详解
  • Agent Skill: react-best-practices
  • 深度测评研究生必用的10款AI论文写作软件
  • 找不到上海智推时代对接方式?这份官方渠道清单收好 - 速递信息