洛谷P3373线段树详解【模板】

洛谷P3373题目概述

洛谷P3373是一道关于线段树的模板题,题目名称为“【模板】线段树 2”。题目的主要要求是对一个长度为 n 的数列进行如下操作:

  1. 将某区间每个数乘上一个数。
  2. 将某区间每个数加上一个数。
  3. 求出某区间所有数的和。

线段树简介

线段树是一种二叉树数据结构,常用于高效处理区间查询和更新操作。它可以将一个区间划分为多个子区间,每个节点代表一个区间,通过维护这些节点的信息,可以在 O ( log ⁡ n ) O(\log n) O(logn) 的时间复杂度内完成区间查询和更新操作。

代码详细讲解

1. 头文件和宏定义
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 400002
#define lson (root*2)
#define rson (root*2+1)
  • #include <iostream>#include <cmath> 分别引入输入输出流和数学库。
  • using namespace std; 表示使用标准命名空间。
  • MAXN 定义了线段树数组的最大长度,通常为 4 n 4n 4n,因为线段树的节点数最多为 4 n 4n 4n
  • lsonrson 分别表示当前节点的左子节点和右子节点的编号。
2. 全局变量
int MOD;
long long sgmt[MAXN], lazy_mul[MAXN], lazy_add[MAXN];
  • MOD 是取模的基数,用于防止结果溢出。
  • sgmt[MAXN] 是线段树数组,用于存储每个节点所代表区间的和。
  • lazy_mul[MAXN]lazy_add[MAXN] 是懒标记数组,分别用于标记乘法和加法操作。
3. push_up 函数
void push_up(int root) {sgmt[root] = sgmt[lson] + sgmt[rson];sgmt[root] %= MOD;
}
  • 该函数用于将左右子节点的信息更新到父节点。具体来说,将左子节点和右子节点所代表区间的和相加,并取模后赋值给父节点。
4. push_down 函数
void push_down(int root, int len) {if (lazy_mul[root] == 1 && lazy_add[root] == 0) return;lazy_mul[lson] *= lazy_mul[root], lazy_mul[lson] %= MOD;lazy_mul[rson] *= lazy_mul[root], lazy_mul[rson] %= MOD;lazy_add[lson] *= lazy_mul[root], lazy_add[lson] %= MOD;lazy_add[rson] *= lazy_mul[root], lazy_add[rson] %= MOD;lazy_add[lson] += lazy_add[root], lazy_add[lson] %= MOD;lazy_add[rson] += lazy_add[root], lazy_add[rson] %= MOD;sgmt[lson] *= lazy_mul[root], sgmt[lson] %= MOD;sgmt[lson] += (len/2)*lazy_add[root], sgmt[lson] %= MOD;sgmt[rson] *= lazy_mul[root], sgmt[rson] %= MOD;sgmt[rson] += (len/2)*lazy_add[root], sgmt[rson] %= MOD;lazy_mul[root] = 1, lazy_add[root] = 0;
}
  • 该函数用于将当前节点的懒标记下推到左右子节点。
  • 首先判断当前节点是否有懒标记,如果 lazy_mul[root] 为 1 且 lazy_add[root] 为 0,则说明没有懒标记,直接返回。
  • 然后将当前节点的乘法懒标记和加法懒标记下推到左右子节点,并更新左右子节点的线段树数组和懒标记数组。
  • 最后将当前节点的懒标记重置为初始值。
5. update 函数
void update(int a, int b, long long c, long long d, int l, int r, int root) {if (a <= l && r <= b) {sgmt[root] *= c, sgmt[root] %= MOD;sgmt[root] += (r-l+1)*d, sgmt[root] %= MOD;lazy_mul[root] *= c, lazy_mul[root] %= MOD;lazy_add[root] *= c, lazy_add[root] %= MOD;lazy_add[root] += d, lazy_add[root] %= MOD;return;}push_down(root,r-l+1);int mid = (l+r)/2;if (a <= mid) update(a,b,c,d,l,mid,lson);if (b > mid) update(a,b,c,d,mid+1,r,rson);push_up(root);
}
  • 该函数用于更新区间 [a, b] 内的所有数,将每个数先乘上 c,再加上 d
  • 如果当前节点所代表的区间完全包含在 [a, b] 内,则直接更新当前节点的线段树数组和懒标记数组。
  • 否则,先将当前节点的懒标记下推,然后递归更新左右子节点,最后更新当前节点的线段树数组。
6. query 函数
long long query(int a, int b, int l, int r, int root) {if (a <= l && r <= b) return sgmt[root];push_down(root,r-l+1);long long ans = 0;int mid = (l+r)/2;if (a <= mid) ans += query(a,b,l,mid,lson), ans %= MOD;if (b > mid) ans += query(a,b,mid+1,r,rson), ans %= MOD;return ans;
}
  • 该函数用于查询区间 [a, b] 内所有数的和。
  • 如果当前节点所代表的区间完全包含在 [a, b] 内,则直接返回当前节点的线段树数组的值。
  • 否则,先将当前节点的懒标记下推,然后递归查询左右子节点,并将结果相加。
7. build 函数
void build(int l, int r, int root) {if (l == r) return;int mid = (l+r)/2;build(l,mid,lson);build(mid+1,r,rson);push_up(root);return;
}
  • 该函数用于构建线段树。
  • 如果当前节点所代表的区间只有一个元素,则直接返回。
  • 否则,递归构建左右子节点,然后更新当前节点的线段树数组。
8. main 函数
int main() {for (int i = 1; i < MAXN; i++)lazy_mul[i] = 1;int n, m; cin >> n >> m >> MOD;int npot = 1 << (int)ceil(log2(n));for (int i = 0; i < n; i++)cin >> sgmt[npot+i];n = npot;int l = 1, r = n, root = 1;build(l,r,root);while (m--) {int op, a, b, c; cin >> op >> a >> b;if (op == 1) {cin >> c; update(a,b,c,0,l,r,root);} else if (op == 2) {cin >> c; update(a,b,1,c,l,r,root);} elsecout << query(a,b,l,r,root)%MOD << endl;}return 0;
}
  • 首先将所有乘法懒标记初始化为 1。
  • 然后读取数列的长度 n、操作的次数 m 和取模的基数 MOD
  • 接着将数列存储在线段树数组中,并将 n 扩展为 2 的幂次方。
  • 调用 build 函数构建线段树。
  • 最后循环处理 m 次操作,根据操作类型调用 updatequery 函数。

复杂度分析

  • 时间复杂度:每次更新和查询操作的时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn),因此总的时间复杂度为 O ( m log ⁡ n ) O(m \log n) O(mlogn)
  • 空间复杂度:线段树数组和懒标记数组的空间复杂度均为 O ( 4 n ) O(4n) O(4n),因此总的空间复杂度为 O ( n ) O(n) O(n)

完整代码:

// P3373 【模板】线段树 2
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 400002
#define lson (root*2)
#define rson (root*2+1)int MOD;
long long sgmt[MAXN], lazy_mul[MAXN], lazy_add[MAXN];void push_up(int root) {sgmt[root] = sgmt[lson] + sgmt[rson];sgmt[root] %= MOD;
}void push_down(int root, int len) {if (lazy_mul[root] == 1 && lazy_add == 0) return;lazy_mul[lson] *= lazy_mul[root], lazy_mul[lson] %= MOD;lazy_mul[rson] *= lazy_mul[root], lazy_mul[rson] %= MOD;lazy_add[lson] *= lazy_mul[root], lazy_add[lson] %= MOD;lazy_add[rson] *= lazy_mul[root], lazy_add[rson] %= MOD;lazy_add[lson] += lazy_add[root], lazy_add[lson] %= MOD;lazy_add[rson] += lazy_add[root], lazy_add[rson] %= MOD;sgmt[lson] *= lazy_mul[root], sgmt[lson] %= MOD;sgmt[lson] += (len/2)*lazy_add[root], sgmt[lson] %= MOD;sgmt[rson] *= lazy_mul[root], sgmt[rson] %= MOD;sgmt[rson] += (len/2)*lazy_add[root], sgmt[rson] %= MOD;lazy_mul[root] = 1, lazy_add[root] = 0;
}void update(int a, int b, long long c, long long d, int l, int r, int root) {if (a <= l && r <= b) {sgmt[root] *= c, sgmt[root] %= MOD;sgmt[root] += (r-l+1)*d, sgmt[root] %= MOD;lazy_mul[root] *= c, lazy_mul[root] %= MOD;lazy_add[root] *= c, lazy_add[root] %= MOD;lazy_add[root] += d, lazy_add[root] %= MOD;return;}push_down(root,r-l+1);int mid = (l+r)/2;if (a <= mid) update(a,b,c,d,l,mid,lson);if (b > mid) update(a,b,c,d,mid+1,r,rson);push_up(root);
}long long query(int a, int b, int l, int r, int root) {if (a <= l && r <= b) return sgmt[root];push_down(root,r-l+1);long long ans = 0;int mid = (l+r)/2;if (a <= mid) ans += query(a,b,l,mid,lson), ans %= MOD;if (b > mid) ans += query(a,b,mid+1,r,rson), ans %= MOD;return ans;
}void build(int l, int r, int root) {if (l == r) return;int mid = (l+r)/2;build(l,mid,lson);build(mid+1,r,rson);push_up(root);return;
}int main() {for (int i = 1; i < MAXN; i++)lazy_mul[i] = 1;int n, m; cin >> n >> m >> MOD;int npot = 1 << (int)ceil(log2(n));for (int i = 0; i < n; i++)cin >> sgmt[npot+i];n = npot;int l = 1, r = n, root = 1;build(l,r,root);while (m--) {int op, a, b, c; cin >> op >> a >> b;if (op == 1) {cin >> c; update(a,b,c,0,l,r,root);} else if (op == 2) {cin >> c; update(a,b,1,c,l,r,root);} elsecout << query(a,b,l,r,root)%MOD << endl;}return 0;
}

感谢阅读在这里插入图片描述

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

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

相关文章

【计算机视觉】CV实战项目- COVID 社交距离检测(covid-social-distancing-detection)

COVID 社交距离检测&#xff08;covid-social-distancing-detection&#xff09; 一、项目概述二、项目架构三、环境搭建四、运行项目五、输出结果六、常见问题及解决方法报错1. cv2.error: OpenCV(4.11.0) :-1: error: (-5:Bad argument) in function circle报错2 cv2.circle(…

CMake使用教程

一、CMake 简介 CMake 是一个跨平台的构建工具&#xff0c;用于自动化生成不同平台&#xff08;如 Makefile、Visual Studio、Xcode 等&#xff09;的构建文件。它的核心是编写 CMakeLists.txt 文件&#xff0c;定义项目的构建规则。 二、安装 CMake Linux: sudo apt-get ins…

大模型Rag - 两大检索技术

一、稀疏检索&#xff1a;关键词匹配的经典代表 稀疏检索是一种基于关键词统计的传统检索方法。其基本思想是&#xff1a;通过词频和文档频率来衡量一个文档与查询的相关性。 核心原理 文档和查询都被表示为稀疏向量&#xff08;如词袋模型&#xff09;&#xff0c;只有在词…

LNA设计

设计目的 为后级提供足够的增益以克服后级电路噪声 尽可能小的噪声和信号失真 确保输入和输出端的阻抗匹配 确保信号线性度 评价标准 噪声系数 功率增益 工作频率和带宽 输入信号功率动态范围 端口电压驻波比 稳定性 基于SP模型的LNA设计 直流分析 S参数分析 设计指标 &#xf…

Vue 常见组件及使用方式全解析

一、引言 在 Vue 开发中&#xff0c;组件是构建复杂用户界面的基石。通过使用各种常见组件&#xff0c;我们可以快速搭建出功能丰富、交互性强的应用程序。本文将详细介绍 Vue 开发中一些常见组件及其使用方式。 二、基础 UI 组件 &#xff08;一&#xff09;按钮组件&#…

设计测试用例模板

面试时问你一个场景&#xff0c;要你设计测试用例&#xff0c;你会怎么回答&#xff1f; 面试官让你设计一个功能的测试用例&#xff0c;比如“上传文件功能”&#xff0c;其实就是想考你&#xff1a; 思维是否全面能不能抓住重点会不会分类和使用测试方法有没有考虑异常情况…

Git 解决“Filename too long”问题

在 Windows 系统中使用 Git 时&#xff0c;遇到 Filename too long 错误通常是由于系统默认的路径长度限制&#xff08;260 字符&#xff09;导致的。以下是综合多种场景的解决方案&#xff1a; 一、快速解决方法 启用 Git 长路径支持 通过 Git 配置命令允许处理超长文件名&am…

Spring Boot 3 + SpringDoc:打造接口文档

1、背景公司 新项目使用SpringBoot3.0以上构建&#xff0c;其中需要对外输出接口文档。接口文档一方面给到前端调试&#xff0c;另一方面给到测试使用。 2、SpringDoc 是什么&#xff1f; SpringDoc 是一个基于 Spring Boot 项目的库&#xff0c;能够自动根据项目中的配置、…

Swagger2Refit

把swagger相关接口转成refit格式&#xff0c;以便其他服务调用 使用工具Refitter. Refitter 项目使用教程 Refit Client API Generator for OpenAPI 项目地址: github.com GitCode - 全球开发者的开源社区,开源代码托管平台 安装 Refitter CLI 工具 首先&#xff0c;通过…

【java 13天进阶Day05】数据结构,List,Set ,TreeSet集合,Collections工具类

常见的数据结构种类 集合是基于数据结构做出来的&#xff0c;不同的集合底层会采用不同的数据结构。不同的数据结构&#xff0c;功能和作用是不一样的。数据结构&#xff1a; 数据结构指的是数据以什么方式组织在一起。不同的数据结构&#xff0c;增删查的性能是不一样的。不同…

systemctl管理指令

今天我们来继续学习服务管理指令,接下来才是重头戏-systemctl,那么话不多说,直接开始吧. systemctl管理指令 1.基本语法: systemctl [start | stop | restart | status]服务 注&#xff1a;systemctl指令管理的服务在/usr/lib/ systemd/system查看 2.systemctl设置服务的自…

STM32单片机教程:从零开始打造智能天气时钟

STM32单片机教程&#xff1a;从零开始打造智能天气时钟 大家好&#xff01;今天我想为大家详细介绍一下我们的STM32课程&#xff0c;以及如何从零基础逐步掌握单片机开发技能&#xff0c;最终实现一个完整的智能天气时钟项目。 课程面向人群 本课程主要面向那些已经通过野火…

Neovim插件深度解析:mcphub.nvim如何用MCP协议重构开发体验

在AI与工具链深度融合的今天,Neovim 作为现代开发者的生产力工具,正通过插件生态不断突破边界。mcphub.nvim 作为一款基于 MCP(Model Context Protocol) 协议的插件,重新定义了Neovim与智能工具的交互方式。它不仅简化了MCP服务器的集成与管理,更通过直观的UI和生态整合,…

第33讲|遥感大模型在地学分类中的初探与实战

目录 🧠 一、什么是“遥感大模型”? 📚 二、遥感大模型在地学分类中的优势 📍三、案例:使用 Segment Anything Model (SAM) 进行遥感地物分割 📦 1. 安装与依赖配置(PyTorch) 🖼 2. 读取遥感图像(可用 Sentinel-2 伪彩色图) 🔧 3. SAM 模型载入 💡 …

MATLAB - 小车倒立摆的非线性模型预测控制(NMPC)

系列文章目录 目录 系列文章目录 前言 一、摆锤/小车组件 二、系统方程 三、控制目标 四、控制结构 五、创建非线性 MPC 控制器 六、指定非线性设备模型 七、定义成本和约束 八、验证非线性 MPC 控制器 九、状态估计 十、MATLAB 中的闭环仿真 十一、使用 MATLAB 中…

JAVA文件I/O

目录 一、三种路径的分类&#xff1a; 1、绝对路径&#xff1a; 2、相对路径&#xff1a; 3、基准目录&#xff1a; 二、文件的种类&#xff1a; 三、利用JAVA操作文件&#xff1a; 1、File类的构造方法&#xff1a; 2、File 类方法的使用&#xff1a; 使用例子&#…

焊接机器人的设计

一、引言 随着制造业的发展&#xff0c;焊接工艺在各个领域得到广泛应用。焊接机器人具有焊接质量高、效率高、劳动强度低等优点&#xff0c;能够满足现代制造业对焊接生产的要求。设计一款性能优良的焊接机器人&#xff0c;对于提高焊接生产的自动化水平和产品质量具有重要意…

Thymeleaf简介

在Java中&#xff0c;模板引擎可以帮助生成文本输出。常见的模板引擎包括FreeMarker、Velocity和Thymeleaf等 Thymeleaf是一个适用于Web和独立环境的现代服务器端Java模板引擎。 Thymeleaf 和 JSP比较&#xff1a; Thymeleaf目前所作的工作和JSP有相似之处&#xff0c;Thyme…

(论文阅读)RNNoise 基于递归神经网络的噪声抑制库

RNNoise 是一个基于递归神经网络的噪声抑制库。 有关该算法的描述见以下论文&#xff1a; J.-M. Valin, A Hybrid DSP/Deep Learning Approach to Real-Time Full-Band Speech Enhancement, Proceedings of IEEE Multimedia Signal Processing (MMSP) Workshop, arXiv:1709.08…

DevOps-文章目录

01什么是DevOps 02DevOps基础环境准备 03-DevOps-安装并初始化Gitlab 04-DevOps-安装并初始化Jenkins 05-DevOps-Jenkins自动拉取构建代码1 05-DevOps-Jenkins自动拉取构建代码2 06-DevOps-自动构建Docker镜像 07-DevOps-安装部署Harbor镜像仓库 08-DevOps-向Harbor上传自定义镜…