洛谷P3372 【模板】线段树 1以及分块
【模板】线段树 1
题目描述
如题,已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上 k k k。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 n , m n, m n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。
接下来 m m m 行每行包含 3 3 3 或 4 4 4 个整数,表示一个操作,具体如下:
1 x y k:将区间 [ x , y ] [x, y] [x,y] 内每个数加上 k k k。2 x y:输出区间 [ x , y ] [x, y] [x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
样例 #1
样例输入 #1
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
样例输出 #1
11
8
20
提示
对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n≤8, m ≤ 10 m \le 10 m≤10。
对于 70 % 70\% 70% 的数据: n ≤ 10 3 n \le {10}^3 n≤103, m ≤ 10 4 m \le {10}^4 m≤104。
对于 100 % 100\% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1≤n,m≤105。
保证任意时刻数列中所有元素的绝对值之和 ≤ 10 18 \le {10}^{18} ≤1018。
【样例解释】
#
思路
先不要看算法标签和题目名称。
通过观察可以发现,如果强行枚举,绝对会超时(不会有玄学方法能过吧 )
看题目的数据范围, 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1≤n,m≤105。
那么这道题目时间复杂度的最高是 O ( n n ) O(n\sqrt n) O(nn)
这道题目明显做法很多,线段树、树状数组可以用十分优秀的 O ( n log n ) O(n\log n) O(nlogn)的时间复杂度通过。
但是明显线段树、树状数组这些做法太长了不好写。
因此,分块成了一种简单好写,而且时间复杂度较为优秀的思想。
分块的时间复杂度略高于线段树、树状数组,为 O ( n n ) O(n\sqrt n) O(nn),所以分块可以在这一题替代线段树。
分块原理
先考虑暴力,最简单的思路就是每一次操作进行增加就从 l l l枚举到 r r r,增加每一个数,求和就从 l l l枚举到 r r r,进行累加。(这里的 l l l和 r r r代表题目中的 x x x和 y y y)
显然时间大大滴超。
导致超时的情况是 l l l和 r r r的差值很大,接下来考虑如何优化掉这种情况。
可以将数组分成很多块。
在 l l l和 r r r的差值很大的情况下,这个区间将会覆盖很多整块。
比如 n = 1000 n=1000 n=1000的情况下,每个块的大小为 100 100 100时,假如 l = 10 l=10 l=10, r = 500 r=500 r=500时,这个区间就可以看作4个完整块以及一个不完整的块,如果可以在 O ( 1 ) O(1) O(1)的时间将每个完整块处理,那么这次操作的时间复杂度就会降到 O ( n ÷ 10 ) O(n÷10) O(n÷10)左右。
分块的块的大小
在面对并不在一个完整的块内的情况,只能进行暴力求解,设块的大小为 s i z siz siz,则时间复杂度为 O ( s i z ) O(siz) O(siz)。
覆盖多个完整块的情况下,每个完整块都是 O ( 1 ) O(1) O(1),设共有 n u m num num块,则时间复杂度为 O ( n u m ) O(num) O(num)。
因为块数=总数÷块的大小,因此 n u m = n / s i z , n u m ∗ s i z = n num=n/siz,num*siz=n num=n/siz,num∗siz=n。
为了让时间复杂度尽可能低,要 n u m num num和 s i z siz siz尽可能都小,此时最好的办法就是将 s i z siz siz和 n u m num num设为 n \sqrt n n。
因此块的大小最好为 n \sqrt n n。
此时的时间复杂度为 m n m\sqrt n mn其中 m m m为操作次数, n n n为元素个数。
这道题的分块实现
所需的数组和变量
ll siz,num;//块个数和块大小
ll L[N],R[N];//每个块的左右端点
ll bel[N],su[N],add[N];//每个元素属于哪一个块,以及每个块的和、每个块每个元素都加上多少
ll n,m,a[N];//对应输入
分块初始化
void init(){siz=sqrt(n);//最佳的分块大小num=n/siz+(n%siz>0);//块的个数for(int i=1;i<=num;i++){//这里预处理,其实也可以封装函数后面计算L[i]=siz*(i-1)+1;R[i]=min(n,siz*i);}for(int i=1;i<=n;i++){//这里也是预处理bel[i]=(i-1)/siz+1;su[bel[i]]+=a[i];}
}
预处理可以降低常数
区间增加某个数
void upd(ll l,ll r,ll k){ll x=bel[l],y=bel[r];if(x==y){//在同一块内的情况for(int i=l;i<=r;i++){a[i]+=k;su[x]+=k;}}else{for(int i=x+1;i<y;i++){//整块add[i]+=k;su[i]+=k*siz;}for(int i=l;i<=R[x];i++){//非完整a[i]+=k;su[x]+=k;}for(int i=L[y];i<=r;i++){//非完整a[i]+=k;su[y]+=k;}}
}
这里可以看作每一个块内所有元素都加上的某个数暂时没有加上去,而是保留到需要用的时候再进行处理。
面对不完整的块直接暴力就行。
区间求和
ll qu(ll l,ll r){ll ans=0;//记录答案ll x=bel[l],y=bel[r];if(x==y){//同一个块内直接暴力就行for(int i=l;i<=r;i++){ans+=a[i]+add[x];//加上add是因为把之前没有加上的加上}}else{for(int i=x+1;i<y;i++){ans+=su[i];//每个块的和之前记录了}for(int i=l;i<=R[x];i++){ans+=a[i]+add[x];//这里也是一样}for(int i=L[y];i<=r;i++){ans+=a[i]+add[y];//同上}}return ans;
}
查找时,之前没有加上去放在add数组里的值就可以加上了,优化了时间复杂度。
可以看出分块还是一种暴力。
这里也可以看出预处理的作用。
“完整”代码
直接复制代码不是一个好习惯。
ll siz,num,L[N],R[N],bel[N],su[N],add[N];
ll n,m,a[N];
void init(){siz=sqrt(n);num=n/siz+(n%siz>0);for(int i=1;i<=num;i++){L[i]=siz*(i-1)+1;R[i]=min(n,siz*i);}for(int i=1;i<=n;i++){bel[i]=(i-1)/siz+1;su[bel[i]]+=a[i];}
}
void upd(ll l,ll r,ll k){ll x=bel[l],y=bel[r];if(x==y){for(int i=l;i<=r;i++){a[i]+=k;su[x]+=k;}}else{for(int i=x+1;i<y;i++){add[i]+=k;su[i]+=k*siz;}for(int i=l;i<=R[x];i++){a[i]+=k;su[x]+=k;}for(int i=L[y];i<=r;i++){a[i]+=k;su[y]+=k;}}
}
ll qu(ll l,ll r){ll ans=0;ll x=bel[l],y=bel[r];if(x==y){for(int i=l;i<=r;i++){ans+=a[i]+add[x];}}else{for(int i=x+1;i<y;i++){ans+=su[i];}for(int i=l;i<=R[x];i++){ans+=a[i]+add[x];}for(int i=L[y];i<=r;i++){ans+=a[i]+add[y];}}return ans;
}
int main(){scanf("%lld %lld",&n,&m);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}init();for(int i=1;i<=m;i++){int op;scanf("%d",&op);if(op==1){ll x,y,k;scanf("%lld %lld %lld",&x,&y,&k);upd(x,y,k);}else{ll x,y;scanf("%lld %lld",&x,&y);ll tmp=qu(x,y);printf("%lld\n",tmp);}}return 0;
}
分块和线段树对比的优势和劣势
时间复杂度上
线段树的时间复杂度平均 O ( n log n ) O(n\log n) O(nlogn)
分块时间复杂度 O ( n n ) O(n\sqrt n) O(nn)
分块时间复杂度较高
空间复杂度上
线段树和分块的空间复杂度均为 O ( n ) O(n) O(n),but实际上分块的空间更小,不容易被卡。
代码复杂度上
d a l a o dalao dalao们的线段树代码100行左右。
分块80行左右,代码量要少。
线段树的思路比较难理解。
分块的思路就是暴力,十分容易理解,分块更好。
结果
可以看出分块与线段树不相上下,面对大部分区间问题分块足以。不行就用莫队
相关文章:
洛谷P3372 【模板】线段树 1以及分块
【模板】线段树 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 将某区间每一个数加上 k k k。求出某区间每一个数的和。 输入格式 第一行包含两个整数 n , m n, m n,m,分别表示该数列数字的个数和操作的总个数。 …...
(动态规划基础 打家劫舍)leetcode 198
已知h2和h1,用已知推出未知 推是求答案,回溯是给答案 这里图片给出dfs暴力,再进行记录答案完成记忆化搜索,再转为dp数组 #include<iostream> #include<vector> #include<algorithm> //nums:2,1,1,2 //dp:2,2,…...
Python 梯度下降法(四):Adadelta Optimize
文章目录 Python 梯度下降法(四):Adadelta Optimize一、数学原理1.1 介绍1.2 实现流程 二、代码实现2.1 函数代码2.2 总代码 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法(四):Adadelta Opt…...
旅行的意义:“诗与远方”和在旅途中找寻真我
原文链接:旅行的意义:“诗与远方”和在旅途中找寻真我 困在格子间,心向远方 清晨,闹钟催促,打工人挣扎起床出门。地铁拥挤,工作繁忙,加班成常态,下班时夜幕已深,满心疲惫…...
leetcode——将有序数组转化为二叉搜索树(java)
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9] 输出:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视为正确答…...
6 齐次坐标模块(homogen.rs)
homogen.rs代码定义了一个名为 HomogeneousVector 的结构体,它是用于表示三维空间中的齐次向量。齐次向量常用于计算机图形学和几何学中,特别是在处理投影和变换时。下面是对这段代码的详细解释和一些关键的代码片段分析: 一、homogen.rs文件…...
007 JSON Web Token
文章目录 https://doc.hutool.cn/pages/jwt/#jwt%E4%BB%8B%E7%BB%8D JWT是一种用于双方之间安全传输信息的简洁的、URL安全的令牌标准。这个标准由互联网工程任务组(IETF)发表,定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。…...
Cursor 背后的技术栈:从 VS Code 到 AI 集成
引言 在当今快速发展的软件开发领域,开发者工具正在经历一场由人工智能(AI)驱动的革命。Cursor 作为一款新兴的智能编程助手,凭借其强大的 AI 能力和高效的开发体验,迅速吸引了大量开发者的关注。Cursor 不仅继承了 V…...
【Python蓝桥杯备赛宝典】
文章目录 一、基础数据结构1.1 链表1.2 队列1.3 栈1.4 二叉树1.5 堆二、基本算法2.1 算法复杂度2.2 尺取法2.3 二分法2.4 三分法2.5 倍增法和ST算法2.6 前缀和与差分2.7 离散化2.8 排序与排列2.9 分治法2.10贪心法1.接水时间最短问题2.糖果数量有限问题3.分发时间最短问题4.采摘…...
前端js高级25.1.30
原型:函数的组成结构 通过这个图我们需要知道。 假设我们创建了一个Foo函数。 规则:Function.protoType是函数显示原型。__proto__是隐式对象。 Function、Object、Foo函数的__proto__指向了Function.protoType说明。这三个都依托function函数来创建。…...
【后端开发】字节跳动青训营之性能分析工具pprof
性能分析工具pprof 一、测试程序介绍二、pprof工具安装与使用2.1 pprof工具安装2.2 pprof工具使用 资料链接: 项目代码链接实验指南pprof使用指南 一、测试程序介绍 package mainimport ("log""net/http"_ "net/http/pprof" // 自…...
云原生(五十二) | DataGrip软件使用
文章目录 DataGrip软件使用 一、DataGrip基本使用 二、软件界面介绍 三、附件文件夹到项目中 四、DataGrip设置 五、SQL执行快捷键 DataGrip软件使用 一、DataGrip基本使用 1. 软件界面介绍 2. 附加文件夹到项目中【重要】 3. DataGrip配置 快捷键使用:C…...
FreeRTOS学习 --- 任务调度
开启任务调度器 作用:用于启动任务调度器,任务调度器启动后, FreeRTOS 便会开始进行任务调度 该函数内部实现,如下: 1、创建空闲任务(优先级最低) 2、如果使能软件定时器,则创建定…...
2025年人工智能技术:Prompt与Agent的发展趋势与机遇
文章目录 一、Prompt与Agent的定义与区别(一)定义(二)区别二、2025年Prompt与Agent的应用场景(一)Prompt的应用场景(二)Agent的应用场景三、2025年Prompt与Agent的适合群体(一)Prompt适合的群体(二)Agent适合的群体四、2025年Prompt与Agent的发展机遇(一)Prompt的…...
区块链 智能合约安全 | 回滚攻击
视频教程在我主页简介和专栏里 目录: 智能合约安全 回滚攻击 总结 智能合约安全 回滚攻击 回滚攻击的本质是”耍赖” 举一个简单的例子,两个人玩石头剪刀布,输了的给对方10块钱,现在A输了,A说这把不算,重来 放在Solidity中,require()函数会检测其中的条件是否满…...
【Go语言圣经】第六节:方法
第六章:方法 6.1 方法声明 在函数声明时,在其名字之前放上一个变量,这就是声明了变量对应类型的一个方法,相当于为这种类型定义了一个独占的方法。 下例为 Point 类型声明了计算两个点之间距离的方法: package mai…...
【JavaEE进阶】图书管理系统 - 壹
目录 🌲序言 🌴前端代码的引入 🎋约定前后端交互接口 🚩接口定义 🍃后端服务器代码实现 🚩登录接口 🚩图书列表接口 🎄前端代码实现 🚩登录页面 🚩…...
TensorFlow 简单的二分类神经网络的训练和应用流程
展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括: 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中,数据准备是通过两个 Numpy 数…...
docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜像…...
Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview
Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview 文章目录 Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview1. Introduction&Abstract1. LLM面临的问题2. RAG核心三要素3. RAG taxonomy 2. Overv…...
LabVIEW透镜多参数自动检测系统
在现代制造业中,提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统,该系统能够在单一工位上完成透镜的多项质量参数检测,并实现透镜的自动搬运与分选,极大地提升了检…...
什么是Maxscript?为什么要学习Maxscript?
MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…...
Redis|前言
文章目录 什么是 Redis?Redis 主流功能与应用 什么是 Redis? Redis,Remote Dictionary Server(远程字典服务器)。Redis 是完全开源的,使用 ANSIC 语言编写,遵守 BSD 协议,是一个高性…...
128周二复盘(164)学习任天堂
1.设计相关 研究历史上某些武器数值,对一些设定进行参数修改。兼顾真实性,合理性,娱乐性。 学习宫本茂游戏思想,简单有趣-重玩性,风格化个性化-反拟真。对堆难度与内容的反思。 后续将学习岩田聪以及别的任天堂名人的…...
LeetCode:63. 不同路径 II
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:63. 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0]…...
VUE之组件通信(一)
1、props 概述:props是使用频率最高的一种通信方式,常用与:父<——>子。 若 父传子:属性值是非函数。若 子传父:属性值是函数。 父组件: <template><div class"father">&l…...
ESP32-S3模组上跑通esp32-camera(41)
接前一篇文章:ESP32-S3模组上跑通esp32-camera(40) 一、OV5640初始化 2. 相机初始化及图像传感器配置 上一回继续对reset函数的后一段代码进行解析。为了便于理解和回顾,再次贴出reset函数源码,在components\esp32-camera\sensors\ov5640.c中,如下: static int reset…...
本地部署DeepSeek R1:打造专属私人AI助手指南
在当今人工智能蓬勃发展的浪潮中,DeepSeek R1模型的本地部署为用户带来了全新的体验。它不仅能够保障数据隐私,还具备与商业AI模型相媲美的出色性能。随着计算能力的不断提升以及开源AI社区的日益壮大,用户如今可以在本地运行高性能AI模型&am…...
Redis-布隆过滤器
文章目录 布隆过滤器的特点:实践布隆过滤器应用 布隆过滤器的特点: 就可以把布隆过滤器理解为一个set集合,我们可以通过add往里面添加元素,通过contains来判断是否包含某个元素。 布隆过滤器是一个很长的二进制向量和一系列随机映射函数。 可以用来检索…...
OpenCV 版本不兼容导致的问题
问题和解决方案 今天运行如下代码,发生了意外的错误,代码如下,其中输入的 frame 来自于 OpenCV 开启数据流的读取 """ cap cv2.VideoCapture(RTSP_URL) print("链接视频流完成") while True:ret, frame cap.rea…...
