洛谷P3372 【模板】线段树 1以及分块

news2025/2/2 0:01:44

【模板】线段树 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 k k k
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 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. 1 x y k:将区间 [ x , y ] [x, y] [x,y] 内每个数加上 k k k
  2. 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 n8 m ≤ 10 m \le 10 m10
对于 70 % 70\% 70% 的数据: n ≤ 10 3 n \le {10}^3 n103 m ≤ 10 4 m \le {10}^4 m104
对于 100 % 100\% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1n,m105

保证任意时刻数列中所有元素的绝对值之和 ≤ 10 18 \le {10}^{18} 1018

【样例解释】

#

思路

先不要看算法标签和题目名称。
通过观察可以发现,如果强行枚举,绝对会超时(不会有玄学方法能过吧
看题目的数据范围, 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1n,m105
那么这道题目时间复杂度的最高 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/siznumsiz=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行左右,代码量要少。

线段树的思路比较难理解。
分块的思路就是暴力,十分容易理解,分块更好。

结果

可以看出分块与线段树不相上下,面对大部分区间问题分块足以。不行就用莫队

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

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

相关文章

(动态规划基础 打家劫舍)leetcode 198

已知h2和h1&#xff0c;用已知推出未知 推是求答案&#xff0c;回溯是给答案 这里图片给出dfs暴力&#xff0c;再进行记录答案完成记忆化搜索&#xff0c;再转为dp数组 #include<iostream> #include<vector> #include<algorithm> //nums:2,1,1,2 //dp:2,2,…

Python 梯度下降法(四):Adadelta Optimize

文章目录 Python 梯度下降法&#xff08;四&#xff09;&#xff1a;Adadelta Optimize一、数学原理1.1 介绍1.2 实现流程 二、代码实现2.1 函数代码2.2 总代码 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法&#xff08;四&#xff09;&#xff1a;Adadelta Opt…

旅行的意义:“诗与远方”和在旅途中找寻真我

原文链接&#xff1a;旅行的意义&#xff1a;“诗与远方”和在旅途中找寻真我 困在格子间&#xff0c;心向远方 清晨&#xff0c;闹钟催促&#xff0c;打工人挣扎起床出门。地铁拥挤&#xff0c;工作繁忙&#xff0c;加班成常态&#xff0c;下班时夜幕已深&#xff0c;满心疲惫…

leetcode——将有序数组转化为二叉搜索树(java)

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确答…

前端js高级25.1.30

原型&#xff1a;函数的组成结构 通过这个图我们需要知道。 假设我们创建了一个Foo函数。 规则&#xff1a;Function.protoType是函数显示原型。__proto__是隐式对象。 Function、Object、Foo函数的__proto__指向了Function.protoType说明。这三个都依托function函数来创建。…

【后端开发】字节跳动青训营之性能分析工具pprof

性能分析工具pprof 一、测试程序介绍二、pprof工具安装与使用2.1 pprof工具安装2.2 pprof工具使用 资料链接&#xff1a; 项目代码链接实验指南pprof使用指南 一、测试程序介绍 package mainimport ("log""net/http"_ "net/http/pprof" // 自…

云原生(五十二) | DataGrip软件使用

文章目录 DataGrip软件使用 一、DataGrip基本使用 二、软件界面介绍 三、附件文件夹到项目中 四、DataGrip设置 五、SQL执行快捷键 DataGrip软件使用 一、DataGrip基本使用 1. 软件界面介绍 2. 附加文件夹到项目中【重要】 3. DataGrip配置 快捷键使用&#xff1a;C…

FreeRTOS学习 --- 任务调度

开启任务调度器 作用&#xff1a;用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c; FreeRTOS 便会开始进行任务调度 该函数内部实现&#xff0c;如下&#xff1a; 1、创建空闲任务&#xff08;优先级最低&#xff09; 2、如果使能软件定时器&#xff0c;则创建定…

2025年人工智能技术:Prompt与Agent的发展趋势与机遇

文章目录 一、Prompt与Agent的定义与区别(一)定义(二)区别二、2025年Prompt与Agent的应用场景(一)Prompt的应用场景(二)Agent的应用场景三、2025年Prompt与Agent的适合群体(一)Prompt适合的群体(二)Agent适合的群体四、2025年Prompt与Agent的发展机遇(一)Prompt的…

区块链 智能合约安全 | 回滚攻击

视频教程在我主页简介和专栏里 目录&#xff1a; 智能合约安全 回滚攻击 总结 智能合约安全 回滚攻击 回滚攻击的本质是”耍赖” 举一个简单的例子,两个人玩石头剪刀布,输了的给对方10块钱,现在A输了,A说这把不算,重来 放在Solidity中,require()函数会检测其中的条件是否满…

【JavaEE进阶】图书管理系统 - 壹

目录 &#x1f332;序言 &#x1f334;前端代码的引入 &#x1f38b;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f343;后端服务器代码实现 &#x1f6a9;登录接口 &#x1f6a9;图书列表接口 &#x1f384;前端代码实现 &#x1f6a9;登录页面 &#x1f6a9;…

TensorFlow 简单的二分类神经网络的训练和应用流程

展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括&#xff1a; 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中&#xff0c;数据准备是通过两个 Numpy 数…

docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式&#xff1a; 方式一&#xff1a; -&#xff09;在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -&#xff09;导出 # 导出镜像…

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透镜多参数自动检测系统

在现代制造业中&#xff0c;提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统&#xff0c;该系统能够在单一工位上完成透镜的多项质量参数检测&#xff0c;并实现透镜的自动搬运与分选&#xff0c;极大地提升了检…

什么是Maxscript?为什么要学习Maxscript?

MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

LeetCode:63. 不同路径 II

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;63. 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角&#xff08;即 grid[0][0]…

Redis-布隆过滤器

文章目录 布隆过滤器的特点:实践布隆过滤器应用 布隆过滤器的特点: 就可以把布隆过滤器理解为一个set集合&#xff0c;我们可以通过add往里面添加元素&#xff0c;通过contains来判断是否包含某个元素。 布隆过滤器是一个很长的二进制向量和一系列随机映射函数。 可以用来检索…

【视频+图文详解】HTML基础3-html常用标签

图文教程 html常用标签 常用标签 1. 文档结构 <!DOCTYPE html>&#xff1a;声明HTML文档类型。<html>&#xff1a;定义HTML文档的根元素。<head>&#xff1a;定义文档头部&#xff0c;包含元数据。<title>&#xff1a;设置网页标题&#xff0c;浏览…