线段树的区间(加、乘)修改、区间询问

线段树的区间(加、乘)修改、区间询问

线段树的区间(加、乘)修改、区间询问


学习是一个持续的过程,每一小步都是进步。

———— 我也不知道是谁说的


1. 分析问题:修改、询问

  • 痛点:直接暴力修改区间每个元素,时间复杂度会退化为 O(n) ,效率极低。
  • 优化:懒标记(lazy tag) ,当修改区间完全覆盖线段树中某个节点区间时,不立即更新所有子节点,而是记录修改操作(打标记),等后续查询/修改需要访问子节点时,再下传标记,保证时间复杂度仍为 O(logn)
  • 结构扩展:在节点结构体中加 add 字段,记录待下传的区间修改值(如区间每个元素加 add )。
    核心操作:
  • pushdown():下传懒标记,更新子节点的统计值和子节点的懒标记。
  • update_range:区间修改主逻辑,覆盖时打标记,否则分裂区间,下传标记后递归修改。
  • 例如,在 [1,10] 的线段树中修改 [4,8] 区间的每个值增加 20,找到完全覆盖的节点(如 [4,5][6,8] 等 ),打标记记录 +20,不立即更新子节点;后续查询/修改涉及这些子节点时,通过 pushdown() 下传标记,更新子节点值和标记。

2. 区间修改的关键:lazy-tag

lazy: 修改一个整块区间时,只对这个线段区间进行整体上的修改,其内部每个元素的内容先不做修改。只有当这部分线段的一致性被破坏时,才把变化值传递给子区间。
tag: 对做 lazy 操作的子区间,记录状态

3. 区间加法

  • 下传懒标记:将当前节点的 add 标记传递给左右子节点
void pushdown(int p) {if (tr[p].add) { // 有懒标记需要下传// 更新左孩子tr[lc].sum += tr[p].add * (tr[lc].r   - tr[lc].l + 1);tr[lc].add += tr[p].add;// 更新右孩子tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);tr[rc].add += tr[p].add;// 清除当前节点的懒标记tr[p].add = 0;}
}
  • 区间更新:给区间 [x,y] 每个数加 k
void update_interval(int p, int x, int y, ll k) {// 完全覆盖,直接更新sum和懒标记if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum += k * (tr[p].r - tr[p].l + 1);tr[p].add += k;return ;}pushdown(p); // 递归子节点前先下传懒标记int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_interval(lc, x, y, k); // 左子树有重叠if (y > m) update_interval(rc, x, y, k); // 右子树有重叠tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新当前节点sum
}
  • 完整代码
#include <bits/stdc++.h>
using namespace std;#define N 100010
#define ll long long#define lc p << 1 // 左孩子编号:p*2
#define rc p << 1|1 // 右孩子编号:p*2+1int n, m;
ll w[N]; // 原数组,用ll防止溢出struct tree {ll l, r, sum, add; // add用于懒标记(区间更新专属优化)
};
tree tr[N * 4];// 下传懒标记:将当前节点的add标记传递给左右子节点
void pushdown(int p) {if (tr[p].add) { // 有懒标记需要下传// 更新左孩子tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);tr[lc].add += tr[p].add;// 更新右孩子tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);tr[rc].add += tr[p].add;// 清除当前节点的懒标记tr[p].add = 0;}
}void build(int p, int l, int r) { // p:根节点tr[p] = {l, r, w[l], 0}; // add先置0if(l == r) return ;int m = (l + r) >> 1;build(lc, l, m); // 左子树build(rc, m + 1, r); // 右子树tr[p].sum = tr[lc].sum + tr[rc].sum; // 当前节点sum=左右子树sum之和
}// 区间更新:给区间[x,y]每个数加k
void update_interval(int p, int x, int y, ll k) {// 完全覆盖,直接更新sum和懒标记if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum += k * (tr[p].r - tr[p].l + 1);tr[p].add += k;return ;}pushdown(p); // 递归子节点前先下传懒标记int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_interval(lc, x, y, k); // 左子树有重叠if (y > m) update_interval(rc, x, y, k); // 右子树有重叠tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新当前节点sum
}// 单点更新:给第pos个位置的数加k(修复:添加pushdown调用)
void update_single(int p, int pos, ll k) {// 找到目标叶子节点if (tr[p].l == tr[p].r) {tr[p].sum += k; // 更新叶子节点的sum(单点的和就是自身值)return;}pushdown(p);int m = (tr[p].l + tr[p].r) >> 1;// 判断目标位置在左子树还是右子树,递归更新对应子树if (pos <= m) update_single(lc, pos, k); // 左子树包含目标位置else update_single(rc, pos, k); // 右子树包含目标位置tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新当前节点sum
}// 区间查询(查询区间[x,y]的和)
ll query(int p, int x, int y) {if (x <= tr[p].l && tr[p].r <= y) { // 完全覆盖,直接返回return tr[p].sum;}pushdown(p); // 递归子节点前先下传懒标记int m = (tr[p].l + tr[p].r) >> 1;ll sum = 0;if (x <= m) sum += query(lc, x, y); // 左子树有重叠if (y > m) sum += query(rc, x, y); // 右子树有重叠return sum;
}int main(){ios::sync_with_stdio(false);cin.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i){cin >> w[i];}build(1, 1, n); // 构建线段树,根节点为1,覆盖[1,n]for(int i = 1; i <= m; ++i){int zwy;cin >> zwy;if(zwy == 1){int x, y;ll k;cin >> x >> y >> k;update_interval(1, x, y, k); // 区间更新(指令1)}else if(zwy == 2){int pos; ll k;cin >> pos >> k;update_single(1, pos, k); // 单点更新(指令2)}else{int x, y;cin >> x >> y;cout << query(1, x, y) << '\n'; // 区间查询(指令3)}}return 0;
}

4. 区间加、乘法

  • 下传懒标记:将当前节点的 muladd 标记传递给左右子节点
void pushdown(int p) {// 无孩子if (tr[p].l == tr[p].r) return;// 乘法懒标记if (tr[p].mul != 1) {// 更新左孩子tr[lc].sum *= tr[p].mul;tr[lc].mul *= tr[p].mul; // 乘法标记下传tr[lc].add *= tr[p].mul; // 加法标记乘以mul// 更新右孩子tr[rc].sum *= tr[p].mul;tr[rc].mul *= tr[p].mul;tr[rc].add *= tr[p].mul;// 清除乘法懒标记tr[p].mul = 1;}// 加法懒标记if (tr[p].add != 0) {// 更新左孩子tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);tr[lc].add += tr[p].add; // 加法标记下传// 更新右孩子tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);tr[rc].add += tr[p].add;// 清除加法懒标记tr[p].add = 0;}
}
  • 区间更新:给区间 [x,y] 每个数加或乘 k
// 区间乘法
void update_cheng(int p, int x, int y, ll k) {// 完全覆盖当前区间,直接更新并记录懒标记if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum *= k;tr[p].mul *= k; // 乘法懒标记tr[p].add *= k; // 加法标记乘以kreturn;}pushdown(p); // 下传懒标记int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_cheng(lc, x, y, k); // 更新左子树if (y > m) update_cheng(rc, x, y, k); // 更新右子树tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新当前节点
}// 区间加法
void update_jia(int p, int x, int y, ll k) {// 完全覆盖,直接更新if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum += k * (tr[p].r - tr[p].l + 1);tr[p].add += k;return ;}pushdown(p);int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_jia(lc, x, y, k);if (y > m) update_jia(rc, x, y, k);tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新sum
}
  • 完整代码
#include <bits/stdc++.h>
using namespace std;#define N 100010
#define ll long long#define lc p << 1 // 左孩子编号:p*2
#define rc p << 1|1 // 右孩子编号:p*2+1int n, m;
ll w[N]; // 原数组,用ll防止溢出struct tree {ll l, r, sum; // 区间左边界、右边界、区间和ll add, mul;  // 加法懒标记、乘法懒标记(补充mul,修复问题3)
};
tree tr[N * 4];// 懒标记下传
void pushdown(int p) {// 无孩子if (tr[p].l == tr[p].r) return;// 乘法懒标记if (tr[p].mul != 1) {// 更新左孩子tr[lc].sum *= tr[p].mul;tr[lc].mul *= tr[p].mul; // 乘法标记下传tr[lc].add *= tr[p].mul; // 加法标记乘以mul// 更新右孩子tr[rc].sum *= tr[p].mul;tr[rc].mul *= tr[p].mul;tr[rc].add *= tr[p].mul;// 清除乘法懒标记tr[p].mul = 1;}// 加法懒标记if (tr[p].add != 0) {// 更新左孩子tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);tr[lc].add += tr[p].add; // 加法标记下传// 更新右孩子tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);tr[rc].add += tr[p].add;// 清除加法懒标记tr[p].add = 0;}
}// 构建线段树
void build(int p, int l, int r) {tr[p] = {l, r, 0, 0, 1};if (l == r) {tr[p].sum = w[l];return;}int m = (l + r) >> 1;build(lc, l, m); // 构建左子树build(rc, m + 1, r); // 构建右子树tr[p].sum = tr[lc].sum + tr[rc].sum;
}// 区间乘法
void update_cheng(int p, int x, int y, ll k) {// 完全覆盖当前区间,直接更新并记录懒标记if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum *= k;tr[p].mul *= k; // 乘法懒标记tr[p].add *= k; // 加法标记乘以kreturn;}pushdown(p); // 下传懒标记int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_cheng(lc, x, y, k); // 更新左子树if (y > m) update_cheng(rc, x, y, k); // 更新右子树tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新当前节点
}// 区间加法
void update_jia(int p, int x, int y, ll k) {// 完全覆盖,直接更新if (x <= tr[p].l && tr[p].r <= y) {tr[p].sum += k * (tr[p].r - tr[p].l + 1);tr[p].add += k;return ;}pushdown(p);int m = (tr[p].l + tr[p].r) >> 1;if (x <= m) update_jia(lc, x, y, k);if (y > m) update_jia(rc, x, y, k);tr[p].sum = tr[lc].sum + tr[rc].sum; // 回溯更新sum
}// 区间查询
ll query(int p, int x, int y) {if (x <= tr[p].l && tr[p].r <= y) { // 完全覆盖,直接返回当前区间sumreturn tr[p].sum;}pushdown(p);int m = (tr[p].l + tr[p].r) >> 1;ll sum = 0;if (x <= m) sum += query(lc, x, y);if (y > m) sum += query(rc, x, y);return sum;
}int main(){ios::sync_with_stdio(false);cin.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i){cin >> w[i];}build(1, 1, n);for(int i = 1; i <= m; ++i){int zwy;cin >> zwy;if(zwy == 1){int x, y;ll k;cin >> x >> y >> k;update_cheng(1, x, y, k);}else if(zwy == 2){int x, y;ll k;cin >> x >> y >> k;update_jia(1, x, y, k);}else{int x, y;cin >> x >> y;cout << query(1, x, y) << '\n'; }}return 0;
}

5. 知识点小结

  • 线段树的核心优化是懒标记 (lazy tag),核心思想是延迟更新,将区间修改的时间复杂度从 O(n) 优化为 O(logn)
  • 懒标记的核心操作是pushdown(),只有在需要访问子节点时才下传标记,保证每次操作的效率。
  • 单加法懒标记只需维护add,初始化值为 0;加 + 乘复合懒标记需要维护 addmul,初始化mul=1add=0,且下传顺序必须先乘后加。
  • 线段树数组的空间必须开原数组的 4 倍,防止越界。
  • 所有区间修改、区间查询操作的递归逻辑一致:完全覆盖则直接处理,否则分裂区间递归处理,最后回溯更新当前节点信息。
  • 线段树的核心适用场景:高频的区间修改 + 高频的区间查询类问题。

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

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

相关文章

2026年医学论文降AI率工具推荐,专业术语不被误改 - 还在做实验的师兄

医学论文专业术语多,降AI时最担心药名、疾病名被误改。推荐嘎嘎降AI(4.8元/千字,术语保留好)和比话降AI(8元/千字,效果极致)。处理完重点检查专业术语即可。2026年医学论文降AI率工具推荐,专业术语不被误改TL;…

MySQL_分组统计

使用group by字句对列进行分组使用having字句对分别组后的结果进行过滤#演示group by having的使用 #部门表 CREATE TABLE dept( deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, dname VARCHAR(20) NOT NULL DEFAULT "", loc VARCHAR(13) NOT NULL DEFAULT…

基于非合作博弈的风-光-氢微电网容量优化配置Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f447; 关注我领取海量matlab电子书和数学建模资料 &#…

MySQL_字符串函数

-- 演示字符串相关函数的使用 &#xff0c; 使用emp表来演示 -- CHARSET(str) 返回字串字符集 SELECT CHARSET(ename) FROM emp; -- CONCAT (string2 [,... ]) 连接字串, 将多个列拼接成一列 SELECT CONCAT(ename, 工作是 , job) FROM emp;-- INSTR (string ,substring ) 返…

吐血推荐!9款AI论文工具测评,本科生写论文必备

吐血推荐&#xff01;9款AI论文工具测评&#xff0c;本科生写论文必备 2026年AI论文工具测评&#xff1a;为什么你需要这份指南&#xff1f; 随着人工智能技术的不断进步&#xff0c;AI论文工具逐渐成为本科生撰写学术论文的重要助手。然而&#xff0c;面对市场上种类繁多、功能…

基于自适应遗传算法的分布式电源优化配置Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f447; 关注我领取海量matlab电子书和数学建模资料 &#…

批量发送请求后立即返回线程的处理结果(不阻塞)

基于Spring Boot的CompletableFuture与Consumer批量处理方案 方案概述: 批量请求处理:用户发送多条数据请求,并发处理每条数据线程池执行:使用自定义线程池处理具体业务逻辑 异步结果获取:通过CompletableFuture获…

GESP认证C++编程真题解析 | 202506 四级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现! 本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战! 专栏特色 1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的…

豆包写的论文怎么过AIGC检测?实测这几款工具有效 - 还在做实验的师兄

豆包生成的论文AI率通常在70%-90%,直接提交肯定过不了。推荐用嘎嘎降AI(85%→8%,4.8元/千字)或比话降AI(可降至5%以下)处理。让豆包自己改没用。豆包写的论文怎么过AIGC检测?实测这几款工具有效TL;DR:豆包生成…

GESP认证C++编程真题解析 | 202506 三级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现! 本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战! 专栏特色 1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的…

Hitler the justice

I hate this world, only Heiliges Rmisches Reich gives me meaning. I love Deutschland and hate the bullies, because I can not find the common love.

DeepSeek写的论文怎么降AI率?3款工具实测对比 - 还在做实验的师兄

DeepSeek写的论文直接提交知网检测,AI率通常在38%-99%之间。单靠DeepSeek自己降AI效果有限(平均降35%),推荐用专业工具:嘎嘎降AI性价比高,比话降AI可将AI率降至0%。DeepSeek写的论文怎么降AI率?3款工具实测对比…

GESP认证C++编程真题解析 | 202506 一级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现! 本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战! 专栏特色 1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的…

2026年期刊论文降AI率工具推荐,顺利发表必备 - 还在做实验的师兄

期刊对AI率的要求通常比学校更严格,部分期刊要求低于10%甚至5%。推荐比话降AI(可降至0%,8元/千字)和嘎嘎降AI(达标率99.26%,4.8元/千字)。投稿前一定要处理好AI率。2026年期刊论文降AI率工具推荐,顺利发表必备…

详细介绍:任务悬赏小程序核心玩法 + 功能全解析:精准匹配与信任构建的变现逻辑

详细介绍:任务悬赏小程序核心玩法 + 功能全解析:精准匹配与信任构建的变现逻辑pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

毕业论文降AI率,学长推荐的5款工具(含免费额度) - 还在做实验的师兄

毕业论文AIGC检测不过别慌,这5款降AI工具都有免费额度可以先体验。重点推荐嘎嘎降AI(免费试用+达标率99.26%)和比话降AI(500字免费+知网专精)。先免费测效果,满意再付费。毕业论文降AI率,学长推荐的5款工具(含…

GESP认证C++编程真题解析 | 202506 二级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现! 本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战! 专栏特色 1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的…

基于SSM的服装销售系统-计算机毕业设计源码+无LW文档

基于SSM的服装销售系统论文 摘要&#xff1a;本文围绕基于SSM&#xff08;Spring、SpringMVC、MyBatis&#xff09;框架的服装销售系统展开研究。阐述了系统的研究背景与意义&#xff0c;深入分析系统需求&#xff0c;详细介绍功能设计。该系统旨在解决传统服装销售管理模式的问…

嘎嘎降AI vs 比话降AI:2026年降AI工具对比测评 - 还在做实验的师兄

嘎嘎降AI主打性价比(4.8元/千字,达标率99.26%),比话降AI主打知网专精(8元/千字,可降至0%)。追求省钱选嘎嘎,追求极致效果选比话。两款都很靠谱,按需选择即可。嘎嘎降AI vs 比话降AI:2026年降AI工具对比测评T…

大数据领域必备:ClickHouse 全方位解析

大数据领域必备&#xff1a;ClickHouse 全方位解析 一、引言 (Introduction) 钩子 (The Hook) “昨天凌晨3点&#xff0c;我被运维的电话叫醒——数据 dashboard 又崩了。” 这是我做大数据工程师时最难忘的经历。当时我们用Hive处理用户行为数据&#xff0c;每次业务方要查“过…