单侧递归线段树

news/2025/12/8 12:42:55/文章来源:https://www.cnblogs.com/Airland/p/19320282

数据结构。


假如说我们现在需要维护一个关于单调栈的上的一些东西,比如结束时单调栈的大小,要求支持修改,就可以考虑使用单侧递归线段树。

其核心就是类似线段树二分一样,每次上传更新就在右子树二分出来第一个可以接上的答案,然后统计在这右边的答案,每次只会递归一侧,所以就叫单侧递归线段树。

在线段树中每个节点需要额外维护目前子树中的最大值,方便更新。

楼房重建

模板题,我们考虑如何在线段树上维护单调栈的大小,每个节点维护子树内的大小和最大值,然后更新做这样一个事情。

  • 继承左子树答案
  • 在右子树二分的同时加上右子树的答案。

具体实现可以看代码。

点击查看代码
//これも運命じゃないか
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f = 1, t = 0; char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}while(ch >= '0' && ch <= '9'){t = t * 10 + ch - '0'; ch = getchar();}return t * f;}inline void write(int x){if(x < 0){putchar('-'); x = -x;}if(x >= 10){write(x / 10);}putchar(x % 10 + '0');}
}
using namespace io;
int n, m;
const int N = 1e5 + 10;
struct Segment{int l, r;int dat;double maxx;
}seg[N * 4];
double a[N];
int ask(int p, double val){if(seg[p].maxx <= val) return 0;if(seg[p].l == seg[p].r){return seg[p].maxx > val;}if(seg[p * 2].maxx >= val){return ask(p * 2, val) + seg[p].dat - seg[p * 2].dat;}else{return ask(p * 2 + 1, val);}
}
void upd(int p){seg[p].maxx = max(seg[p * 2].maxx, seg[p * 2 + 1].maxx);seg[p].dat = seg[p * 2].dat + ask(p * 2 + 1, seg[p * 2].maxx);
}
void build(int p, int l, int r){seg[p].l = l;seg[p].r = r;seg[p].maxx = -1;if(seg[p].l == seg[p].r) {return ;}int mid = (l + r) >> 1;build(p * 2, l, mid);build(p * 2 + 1, mid + 1, r);
}
void change(int p, int x, double v){if(seg[p].l == seg[p].r){seg[p].dat = 1;seg[p].maxx = v;return ;}int mid = (seg[p].l + seg[p].r) >> 1;if(x <= mid){change(p * 2, x, v);}else{change(p * 2 + 1, x, v);}upd(p);
}
signed main() {
#ifndef Airfreopen("data.in","r",stdin);freopen("data.out","w",stdout);
#endifios::sync_with_stdio(false);cin.tie(0);cout.tie(0);n = read();m = read();build(1, 0, n);change(1, 0, 0);for(int i = 1; i <= m; i++){int id = read();double x = read();x = x / id;change(1, id, x);cout << seg[1].dat - 1 << '\n';}return 0;
}

[HNOI/AHOI2018] 转盘

推式子题,我们考虑答案一定可以通过在起点停留一会,然后再往前走得到。

我们先断环成链,接下来考虑答案怎样表示:

发现还是不好刻画,于是就倒着考虑,就有,其中 \(i\) 是终点:

\[ans - (i - j) \ge t_j \]

\[ans \ge \max \{ t_j - j \} + i \]

接下来把 \(i\) 换成起点:

\[ans = \min_{i = 1}^{n} \max_{j = i}^{i + n - 1} \{ T_j + i - j \} + n - 1 = \min_{i = 1}^{n} \max_{j = i}^{i + n - 1} \{ T_j - j \} + i + n - 1 \]

考虑换元,设 \(T_j - j = a_j\),有:

\[ans = \min_{i = 1}^{n} \max_{j = i}^{i + n - 1} \{ a_j \} + i + n - 1 \]

\(a_i > a_{i+n}\) 恒成立,所以就有:

\[ans = \min_{i = 1}^{n} \max_{j = i}^{2n} \{ a_j \} + i + n - 1 \]

之后我们考虑拆贡献,答案就有:

不妨设 \(i\)\(i < k \to a_i > a_k\)\(j\) 是满足 \(a_j > a_i\) 的最小值

\[ans = \min \{a_i + j + 1 + n - 1 \} = \min \{ a_i + j \} + n \]

然后这个就相当于是在一个单调栈上维护信息,用单侧递归线段树即可。

点击查看代码
//これも運命じゃないか
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f = 1, t = 0; char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}while(ch >= '0' && ch <= '9'){t = t * 10 + ch - '0'; ch = getchar();}return t * f;}inline void write(int x){if(x < 0){putchar('-'); x = -x;}if(x >= 10){write(x / 10);}putchar(x % 10 + '0');}
}
using namespace io;
int n, m, q;
const int N = 2e5 + 10;
int a[N];
struct Segment{int l, r;int dat;//全局的答案int datl;//记录左侧的答案int maxx;
}seg[N * 4];
int ask(int p, int val){if(seg[p].l == seg[p].r){return seg[p].maxx > val ? val + seg[p].l : 1e18;}if(seg[p * 2 + 1].maxx > val){return min(seg[p].datl, ask(p * 2 + 1, val));}else{return ask(p * 2, val);}
}
void upd(int p){seg[p].maxx = max(seg[p * 2].maxx, seg[p * 2 + 1].maxx);seg[p].datl = ask(p, seg[p * 2 + 1].maxx);seg[p].dat = min(seg[p].datl, seg[p * 2 + 1].dat);
}
void build(int p, int l, int r){seg[p].l = l;seg[p].r = r;if(l == r){seg[p].datl = seg[p].dat = 1e18;seg[p].maxx = a[l] - l;return ;}int mid = (l + r) >> 1;build(p * 2, l, mid);build(p * 2 + 1, mid + 1, r);upd(p);
}
void change(int p, int x, int v){if(seg[p].l == seg[p].r){seg[p].maxx = v;return ;}int mid = (seg[p].l + seg[p].r) >> 1;if(x <= mid){change(p * 2, x, v);}else{change(p * 2 + 1, x, v);}upd(p);
}
signed main() {
#ifndef Airfreopen(".in","r",stdin);freopen(".out","w",stdout);
#endifios::sync_with_stdio(false);cin.tie(0);cout.tie(0);n = read();q = read();int p = read();for(int i = 1; i <= n; i++){a[i + n] = a[i] = read();}build(1, 1, n * 2);int lastans = min(1 + seg[1].maxx, seg[1].datl) + n;cout << lastans << '\n';while(q--){int l = read(), r = read();l ^= (lastans * p);r ^= (lastans * p);change(1, l, r - l);change(1, l + n, r - l - n);lastans = min(1 + seg[1].maxx, seg[1].datl) + n;cout << lastans << '\n';}return 0;
}

CF1736C2

考虑先表示出答案:

\[ans = \sum_i {i - \max_j^{i} \max\{0, j - a_j \}} \]

然后化简:

\[ans = \frac{n (n + 1)}{2} - \sum_i {\max_j^{i} \max\{0, j - a_j \}} \]

用单侧递归线段树维护即可。

点击查看代码
//これも運命じゃないか
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f = 1, t = 0; char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}while(ch >= '0' && ch <= '9'){t = t * 10 + ch - '0'; ch = getchar();}return t * f;}inline void write(int x){if(x < 0){putchar('-'); x = -x;}if(x >= 10){write(x / 10);}putchar(x % 10 + '0');}
}
using namespace io;
//正难则反
int n;
const int N = 2e5 + 10;
int a[N];
struct Segment{int l, r;int dat;int maxx;
}seg[N * 4];
int ask(int p, int val){if(seg[p].maxx <= val) return (seg[p].r - seg[p].l + 1) * val;if(seg[p].l == seg[p].r){return seg[p].dat;}if(seg[p * 2].maxx <= val){return (seg[p * 2].r - seg[p * 2].l + 1) * val + ask(p * 2 + 1, val);}else{return ask(p * 2, val) + seg[p].dat - seg[p * 2].dat;}
}
void upd(int p){seg[p].maxx = max(seg[p * 2].maxx, seg[p * 2 + 1].maxx);seg[p].dat = seg[p * 2].dat + ask(p * 2 + 1, seg[p * 2].maxx);
}
void build(int p, int l, int r){seg[p].l = l;seg[p].r = r;if(l == r){seg[p].maxx = max(0ll, l - a[l]);//计算贡献区间//相当于计算 \sum \max max(i - a[i] + 1, 1ll)seg[p].dat = seg[p].maxx;return ;}int mid = (l + r) >> 1;build(p * 2, l, mid);build(p * 2 + 1, mid + 1, r);upd(p);// cerr << "Build " << l << ' ' << r << ' ' << seg[p].dat << '\n';
}
void change(int p, int x, int v){if(seg[p].l == seg[p].r){seg[p].maxx = v;seg[p].dat = seg[p].maxx;return ;}int mid = (seg[p].l + seg[p].r) >> 1;if(x <= mid){change(p * 2, x, v);}else{change(p * 2 + 1, x, v);}upd(p);
}
signed main() {
#ifndef Airfreopen(".in","r",stdin);freopen(".out","w",stdout);
#endifios::sync_with_stdio(false);cin.tie(0);cout.tie(0);n = read();for(int i = 1; i <= n; i++){a[i] = read();}build(1, 1, n);int q = read();int tot = n * (n - 1) / 2 + n;// cerr << tot - seg[1].dat << '\n';while(q--){int id = read(), z = read();z = max(0ll, id - z);change(1, id, z);cout << tot - seg[1].dat << '\n';z = max(0ll, id - a[id]);change(1, id, z);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/992667.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析:线性代数 - 奇异值分解(SVD Singular Value Decomposition)- 奇异值在哪里

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

使用 NSSM(Non-Sucking Service Manager) 将各种服务当作系统服务,即使注销用户也能运行|以 Syncthing 为例

1. NSSM 下载 + 安装 # 1. 下载并解压 NSSM $TempDir = "$env:TEMP\nssm_install" $NssmZip = "$TempDir\nssm.zip" New-Item -ItemType Directory -Path $TempDir -Force | Out-Null Invoke-WebR…

那些算法的空间优化

那些算法的空间优化猫树分治 例题:[ABC426G] Range Knapsack Query 朴素实现: #include <iostream> #include <vector> #include <cstring> using namespace std;using ll = long long;constexpr …

找2025台阶仪厂家?锁定高精度,这份选型指南直接抄

找2025台阶仪厂家?锁定高精度,这份选型指南直接抄!台阶仪作为微电子、精密加工、材料研究等领域的核心测量设备,其选型质量直接影响检测数据的可靠性。2025 年选型需围绕实际应用场景,重点关注以下关键维度,确保设…

2025年毛巾专业工厂五大推荐,新测评精选毛巾制造厂

为帮企业高效锁定适配自身需求的毛巾生产合作伙伴,避免选型走弯路,我们从技术落地能力(如材料研发、工艺革新)、产品性能(含清洁效能、耐用性)、全周期服务质量(覆盖定制研发到售后保障)及真实客户口碑(侧重同…

人大金仓kingbase8常用配置和命令

一、配置集群更新授权 1.将授权文件放入数据库目录中替换原有授权问题 2.进入kingbase/bin目录下,执行./sys_ctl reload -D <数据目录> 二、配置集群数据源主节点 192.168.0.111 备节点 192.168.0.112 端口 543…

STM32F030开发环境搭建

STM32F030开发环境搭建$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");纪念文章,当时啥也不会,现在建议直接STM32CubeIDE 长话短说,由于突然要调试别人画…

2025年中国生物标本五大品牌推荐:河南大科实力凸显

TOP1 推荐:河南大科教学仪器有限公司 推荐指数:★★★★★ 口碑评分:河南地区标杆级生物标本企业 专业能力:河南大科教学仪器有限公司是集科研、开发、生产、销售、服务为一体的高新技术企业,核心产品覆盖教学/科…

2025优质阻燃泡棉厂家排行

2025优质阻燃泡棉厂家排行。阻燃泡棉是一种具备防火阻燃性能的泡棉材料,能在接触火焰时抑制燃烧或减缓燃烧速度,减少烟雾和有毒气体产生。它在建筑、汽车、电子、家具、医疗器械等多个行业应用广泛,比如建筑装修中的…

2025年长春五大专业的理想汽车改装公司排行榜,口碑不错的理

为帮理想车主高效锁定适配需求的改装合作伙伴,避免踩坑走弯路,我们从施工技术实力(如技师资质、硬件设备)、服务透明程度(含过程监管、验收标准)、售后保障体系及真实客户口碑四大维度,对多家服务商展开深度评估…

2025国内十大优质输送机厂家实力盘点

在工业生产的物料搬运环节,输送机是串联产线、提升效率的核心设备。当下自动化需求持续增长,输送机市场规模稳步扩大,但也存在设备适配难、售后响应慢等问题,选对厂家成为企业降本增效的关键。本文聚焦 2025 年国内…

冲压机械手生产商哪家好?2025口碑冲床机械手厂家排名

在冲压加工行业里,人工上下料不仅效率低,还容易出现安全事故。如今,冲压机械手成了替代人工的关键设备,能大幅提升生产效率、保障作业安全。但市面上冲压机械手生产商数量多,实力参差不齐,企业想选到靠谱厂家并不…

2025专业化学镍生产线公司TOP5权威推荐:甄选设备供货商

表面处理行业中,企业对高精度、高稳定性的化学镍生产线需求日益迫切。2024年数据显示,国内化学镍设备市场规模突破120亿元,年增速达32%,但31%的投诉集中在成本控制难、工艺稳定性差、废水处理复杂三大痛点——某汽…

2025模切厂家哪家好?精选可靠产品

2025模切厂家哪家好?精选可靠产品。模切是一种常见的材料加工工艺,简单说就是通过模具对纸张、薄膜、金属箔、泡棉等材料进行裁切,做出特定形状的产品或零部件。它在电子、包装、汽车、医疗器械等多个行业都有广泛应…

【学习笔记】Linux 小记

开了一个新坑,正在慢慢填坑中。未完待续。泛滥河水将我冲向你的心头,不停流 ......

丝扣管件厂家有哪些?2025工业管件厂家实力榜单

工业管件是石油化工、电力、储能等多个工业领域的关键配套部件,就像工业系统中的 “连接枢纽”,保障流体传输的稳定与安全。如今,随着工业 4.0 的推进,工业管件行业正朝着自动化、数字化方向发展,精密铸造、智能加…

攻防世界Robots

1根据题目提示与网站协议有关 Robots 协议的配置文件robots.txt通常存放在网站根目录下,因此在题目提供的网址后添加/robots.txt 2找到协议配置文件 3提取Disallow后的路径 4即找到flag flag cyberpeace 总结 1.访…

2025刀闸门厂家推荐综合榜单

2025刀闸门厂家推荐综合榜单。刀闸门是一种通过刀片式闸板切断介质的阀门设备,广泛应用于污水处理、水利工程、矿山冶金、造纸印染等领域,主要用于控制污水、泥浆、矿浆、纸浆等含杂质介质的流通。从行业现状来看,随…

vue 常用的 gantt 甘特图组件推荐

vue 常用的 gantt 甘特图组件推荐,项目中使用过的甘特图组件,各有优缺点。 dhtmlx-gantt 甘特图 https://dhtmlx.com/docs/products/demoApps/ 老牌的甘特图组件,给你非常全面,使用难度高,可定制化功能比较一般,…