当前位置: 首页 > article >正文

一维差分算法篇:高效处理区间加减

在这里插入图片描述

那么在正式介绍我们的一维差分的原理前,我们先来看一下一维差分所应用的一个场景,那么假设我们现在有一个区间为[L,R]的一个数组,那么我要在这个数组中的某个子区间比如[i,m] (L<=i<=m<=R)进行一个加k值或者减去k值的一个操作,那么我们常规的暴力方法就是我们直接遍历一遍该数组的子区间每个位置加k或者每个位置减k,那么一旦该操作多了,那么对数组的遍历就过于频繁,那么我们能不能用一个较小的代价来完成这每个区间的加k值或者减k值的所有操作而不是每次都去遍历一遍数组呢,那么我们的一维差分就能做到这一点。

1.一维差分原理

那么我们现在有一个区间为[L,R]的一个数组,那么我们要在其中的任意的子区间加k或者减k,那么这里我们目标是想得到我们这个数组从原始状态经过该操作后也就是在任意子区间加减k值后的一个最终状态,那么我们对这个数组的任意子区间的加减操作我们可以理解为我们要在数组原状态下所叠加的状态,那么如果我们能够知道我们数组各种叠加状态后的综合得到的一个最终的叠加状态,那么我们数组在此基础上只需要叠加一个综合的叠加状态那么就可以得到最终状态了。

这里我们还是举一个例子来解释一下我刚才的一个观点,那么假设我们有长度为10的一个原数组[1,2,3,4,5,6,7,8,9,10],那么这里假设我们对这个[0,9]区间的数组中的[0,4]区域处整体减去3,在[0,2]区域处整体加2,最后在[3,5]区域处整体加1,那么我们要知道在这三个操作下我们数组最终的状态的各个位置的值是怎样的

那么我们这里可以把我们数组最开始的形态:[1,2,3,4,5,6,7,8,9]视作初始状态,而我们这三次操作比如在[0,4]区域处整体减去3,视作我们要叠加的状态,那么这里我们如果要得到我们的目标数组,我们肯定是分别叠加三次要叠加的状态,比如在[0,4]减3,然后再[0,2]区域加二,最后在[3,5]区域加1,最终得到我们数组的最终状态也就是[0,1,2,2,3,5,6,7,8,9],那么现在我们希望就是只用叠加一次状态就得到我们的最终状态,那么这里我们只用叠加一次,那么也就意味着我们要知道这三次叠加状态的一个综合效果。

那么这里得到这里所谓的综合得到的叠加状态,就是通过建立一个差分数组diff,该数组的下标就对应我们原始数组的相应位置,那么其中的每个位置的值则表示我们原始数组中对应位置最终该叠加的一个状态也就是[-1,-1,-1,-2,-2,0,0,0,0,0],那么我们原始数组每个位置加上对应的值就能够得到我们最终状态也就是[0,1,2,2,3,5,6,7,8,9],也就只需要遍历一遍数组即可。

所以现在的疑问就转变为了如何加载这个差分数组diff,那么这里我们假设我们要在区间[L,R]中的子区间[i,m]处加v,那么这里我们需要一个与我们原始数组长度对应的一个差分数组,然后将其初始化每个位置的大小都为0,然后我们这里只需要在下标为i位置处加v,然后再m+1处加-v,然后对该差分数组加载对应的一份前缀和数组,那么我们就可以把前缀和数组中区间[i,m]的整体的值给全部刷成v,那么我们对于区间[i,m]的每个位置上的数要叠加的状态就是该区间的每个数加v,那么我们通过这两步就能得到区间[i,m]的叠加状态。

看到这里你想必一定有两个疑问,第一问是想问我为什么要在i位置处加v,在m+1位置处要减去v?第二问则是为什么加工一遍前缀和就能够得到该操作下的叠加状态?

这里我们知道我们的叠加状态就是对整个区间统一进行加或者减去定值k,那么这里我们要得到该状态所对应的差分数组的话,那么差分数组中对应[i,m]的值一定全部是k或者-k,那么这里我们如何将差分数组中的[i,m]全部设置为k或者-k呢,当然肯定不是通过遍历,而这时候前缀和派上用场了,因为我们前缀和的计算公式是sum[i]=sum[i-1]+arr[i],那么我们发现如果我们在i位置处加一个v,那么根据前缀和的公式:sum[i]+v=sum[i-1]+(arr[i]+v),那么此时i位置加v之前对于第i+1位置的前缀和是:sum[i+1]=sum[i]+arr[i+1],但是我们由于i位置加了一个v,那么sum[i]此时的值是sum[i]+v,那么对于sum[i+1]来说,由于sum[i+1]=sum[i]+v+arr[i+1],而现在sum[i]的值变成了sum[i]+v,所以sum[i+1]的值就变成了sum[i+1]+v,那么我们可以依次类推:
在这里插入图片描述

那么这里我其实就可以观察到我们在i位置处加v,然后加工一遍前缀和,我们包括i位置在内以及之后的每一个前缀和都会加v,那么这个加v的效果会持续到包括i位置在内的右侧的所有区间,那么根据我们上面的式子,由于我们差分数组的每个数初始化为0,那么也就意味着我们在sum[i]的前缀和是0,那么我们如果不加v的话,那么我们这个差分数组加工得到的前缀和数组的每个位置都是0,但是由于我们这里在i位置加了v,那么我们知道我们在包括i位置之后的右侧区间全部会刷成v,但是我们只要[i,m]处刷成v,那么我们得到m位置之后抵消这个加v的效果,那么我们就在m+1位置从设置-v,那么我们m+1以及右侧的位置都会有一个加-v的效果,那么与前面的抵消了,所以这就是为什么我们在i位置处设置v,m+1位置处设置-v,然后加工一遍前缀和的原因。
在这里插入图片描述

那么我们如果在[i,m]区间处减去v的话,只要你看懂我们上面的原理,那么就不用我再去讲解赘述了,所以我们对于多次这样的操作,比如在某个区间[i,m]加v或者减去v,那么我们就根据上文讲的原理,在对应位置处i位置加或者减去v,然后再相应的m+1位置处减或者加v,然后加工一遍前缀和,就能得到这所有操作综合的叠加状态了


那么这里其实代码模版就很简单,就是一个差分数组和对应加工的前缀和数组,只不过这里在实现的时候注意边界问题,因为我们如果在比如[i,R]区间上加v或者减v,R是我们这个数组的右边界,那么我们的差分数组如果跟原数组长度一样的话,那么根据刚才的原理,我们会在R+1位置处减v或者加v,那么为了不越界的话,我们会加上一些条件判断,但是如果你不想讨论边界的话,你的差分数组的长度就可以比原数组大一个单位,也就是原数组的长度是n,那么你的差分数组1长度就是n+1,然后按照刚才的思路去在相应的位置设置值,最后只需要加工一遍前缀和即可。

公式:

在[i,m]统一加v:diff[i]+v diff[m+1]-v

在[i.m]统一减v:diff[i]-v diff[m+1]+v

代码实现:

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> arr(10) = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};vector<int> diff(arr.size() + 1, 0); // 差分数组长度应为原数组长度+1// 假设要在[0,3]区间加3,在[2,4]区间减2diff[0] += 3;diff[4] -= 3; // 注意这里是4,因为区间是[0,3],所以结束位置+1diff[2] -= 2;diff[5] += 2; // 同样,区间是[2,4],所以结束位置+1vector<int> diffsum(arr.size()+1, 0); for (int i = 1; i < arr.size(); i++) {diffsum[i] = diffsum[i - 1] + diff[i-1];}return 0;
}

那么很多人看完这个一维差分,感觉差分不是很高效,我们加载一遍前缀和意味着要遍历一遍数组,那么复杂度是o(N),那么如果说我们只是对区间[L,R]的数组中某个子区间统一的加或减去v,那么根绝还不如我直接遍历这个子区间高效,但是我们差分数组在面对这样的场景,比如你这里要对数组的子区间进行100次甚至1000多次的加v或者减v操作,如果这个数组还特别长的话,那么此时长分就只需要遍历一遍数组即可,所以此时差分的优秀就体现出了

2.等差数列差分

那么我们这里有一个特殊的差分也就是等差数列差分,那么我们这里假设要在区间[i,m]中叠加的状态是一个等差数列,那么该等差数列首项是s,公差是d,那么i位置是s,往后依次是s+d,s+2d,所以最终的叠加状态:

[s,s+d,s+2d,s+3d,…,s+(i-m)*d]

那么这里我们要得到该叠加状态其实我们加载一遍前缀和是不够的,因为一边前缀和只能将区间刷成一个统一的值比如s比如d,那么这里一遍不够,那么也就意味着我们要加载两边前缀和,那么这里我们要得到我们加载两边前缀和数组的初始差分数组的形态,那么我们就采取逆推

我们这里先推最终叠加状态的上一级状态也就是中间状态,那么我们这里[i.m]每个位置都有首项s,那么这里我们的i位置就得加上s,然后m+1位置处减去s,那么这里由于我们这里i+1到n处的d是依次递增的,那么我们这里i+1到m处肯定都有一个d值,这样加载前缀和,前面的加d的效果到后面一个位置,那么在加上后面本身有一个d,那么该位置就得到2d,那么在往后传递,就能做到递增,但是要在m之后的区间抵消,那么我们就得在m+1位置处减去(i-m)*d

那么我们要得到中间状态,也就是[s,d,d,d,d,d,d,…,-(s+(i-m)*d)]那么我们则需要在l位置设置为s,l+1位置处设置为d-s,然后m+1位置处设置为-(s+(i-m))*d),然后我们加工两遍前缀和数组即可

代码实现:

#include <iostream>
#include <vector>
using namespace std;int main() {int n = 10; // 数组长度int i = 2;  // 区间起始位置int m = 5;  // 区间结束位置int s = 3;  // 等差数列首项int d = 2;  // 等差数列公差vector<int> arr(n, 0); // 原始数组vector<int> diff(n + 1, 0); // 差分数组// 初始化差分数组diff[i] += s;if (i + 1 <= m) {diff[i + 1] += -s + d;}if (m + 1 < n) {diff[m + 1] -= s + (m - i) * d;}// 第一次前缀和,得到中间状态vector<int> mid(n, 0);diff[0]=0for (int j = 0; j < n; j++) {mid[j] = mid[j] + diff[j];}// 第二次前缀和,得到最终状态vector<int> result(n, 0);result[0] = mid[0];for (int j = 1; j < n; j++) {result[j] = result[j - 1] + mid[j];}return 0;
}

3.结语

那么我们差分是我们处理区间加减操作的一个非常常用高效的一个算法,那么这里的差分我们是一维差分,那么既然都说了是一维差分,那么必然就有二维差分,那么我们了解了一维差分的原理之后,那么其实二维差分我们也大概知道它的用途,那么就是针对于二维数组某个区域的统一的加减问题,那么我会在之后的文章中会讲解我们的二维差分以及二维差分需要用到的二维前缀和,那么希望本文能够让你有所收获,我会持续更新,也希望你多多关注与支持,你的支持就是我最大的动力!
在这里插入图片描述

相关文章:

一维差分算法篇:高效处理区间加减

那么在正式介绍我们的一维差分的原理前&#xff0c;我们先来看一下一维差分所应用的一个场景&#xff0c;那么假设我们现在有一个区间为[L,R]的一个数组&#xff0c;那么我要在这个数组中的某个子区间比如[i,m] (L<i<m<R)进行一个加k值或者减去k值的一个操作&#xff…...

export关键字

注意点&#xff1a; 使用 export 和 import 时&#xff0c;确保你的JavaScript环境支持ES6模块 在JavaScript中&#xff0c;export 关键字主要用于模块化编程&#xff0c;允许你将代码的不同部分导出&#xff0c;使得其他模块可以通过 import 关键字来引入这些部分。这是ES6&a…...

【C++】基础入门(详解)

&#x1f31f; Hello&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; 目录 输入&输出 缺省参数(默认参数) 函数重载 引用 概念及定义 特性及使用 const引用 与指针的关系 内联inline和nullptr in…...

【快速入门】Unity 常用组件(功能块)

欢迎关注 、订阅专栏 【unity 新手教程】谢谢你的支持&#xff01;&#x1f49c;&#x1f49c; 文章目录 Unity 常用组件&#xff08;功能块&#xff09;&#xff1a;Transform - 变换&#xff1a;坐标、朝向、大小Mesh Filter - 加载网格数据Mesh Renderer- 渲染网格Camera - …...

Nessus 工具使用全攻略

目录 一、Nessus&#xff1a;网络安全的坚固防线 二、Nessus 安装指南 &#xff08;一&#xff09;获取安装包 &#xff08;二&#xff09;安装流程 三、初次配置&#xff1a;开启 Nessus 的第一步 &#xff08;一&#xff09;账号注册 &#xff08;二&#xff09;激活 …...

1441. 用栈操作构建数组 中等

1441. 用栈操作构建数组 给你一个数组 target 和一个整数 n。每次迭代&#xff0c;需要从 list { 1 , 2 , 3 ..., n } 中依次读取一个数字。 请使用下述操作来构建目标数组 target &#xff1a; "Push"&#xff1a;从 list 中读取一个新元素&#xff0c; 并将其推入…...

【Springboot知识】从零开始配置springfox

文章目录 配置过程1. 添加依赖2. 创建Swagger配置类3. 配置Swagger UI4. 自定义Swagger配置&#xff08;可选&#xff09;4.1 添加全局请求参数4.2 配置响应消息 5. 运行项目并访问Swagger UI6. 其他注意事项7. 使用Springfox 3.x&#xff08;可选&#xff09;总结 忽略登录验证…...

PHP代驾系统小程序

&#x1f697; 代驾系统 —— 安全、便捷、智能的出行新选择 &#x1f527; 一款基于先进ThinkPHPUniapp技术架构&#xff0c;匠心独运的代驾软件横空出世&#xff0c;微信小程序端率先登场&#xff0c;为您的出行之旅增添前所未有的便捷与安全。它不仅是您贴心的出行助手&…...

pg认证需要培训机构吗

认证类型决定是否需要培训机构 官方认证 PostgreSQL社区认证&#xff1a;PostgreSQL社区并未强制要求通过培训机构才能参加认证考试。例如&#xff0c;PostgreSQL Professional Certification&#xff08;由社区认可的机构提供&#xff09;通常允许考生自学后直接报名考试。 Po…...

网络安全扫描--基础篇

前言 1、了解互联网安全领域中日趋重要的扫描技术 2、了解在不同网络场景下扫描技术手段 3、熟悉linux下系统内核防护策略并能大件一个有效的系统防护体系 4、增强工作安全意识&#xff0c;并能有效的实践于工作场景中 目录 1、熟悉主机扫描工具&#xff08;fping&#xff0c;…...

【MySQL数据库】Ubuntu下的mysql

目录 1&#xff0c;安装mysql数据库 2&#xff0c;mysql默认安装路径 3&#xff0c;my.cnf配置文件? 4&#xff0c;mysql运用的相关指令及说明 5&#xff0c;数据库、表的备份和恢复 mysql是一套给我们提供数据存取的&#xff0c;更加有利于管理数据的服务的网络程序。下…...

GPQA (Graduate-Level Google-Proof QA Benchmark) 数据集

标题&#xff1a;挑战人类与AI的极限&#xff1a;GPQA——一个面向未来的高难度科学问答基准 引言 在人工智能快速发展的今天&#xff0c;大型语言模型&#xff08;如GPT-4&#xff09;已能在许多任务中媲美甚至超越人类表现。然而&#xff0c;当面对需要高度专业知识的问题时&…...

WebRTC与EasyRTC:开启智能硬件音视频通讯的全新旅程

在当今数字化时代&#xff0c;音视频通讯技术正以前所未有的速度革新着我们的生活与工作方式。WebRTC与EasyRTC作为这一领域的佼佼者&#xff0c;正携手为智能硬件的音视频通讯注入强大动力&#xff0c;开启全新的篇章。 一、WebRTC与智能硬件融合的崭新趋势 WebRTC技术&…...

利用ffplay播放udp组播视频流

ffplay -fs -fflags nobuffer -flags low_delay -analyzeduration 0 -probesize 32 -framedrop -sync ext -strict experimental udp://224.1.1.1:5001 -fs : 全屏显示 -fflags nobuffer &#xff1a; 禁用输入缓冲&#xff08;减少100-200ms缓冲延迟&#xff09; -an…...

基于Ceedling的嵌入式软件单元测试

Ceedling 如果你使用 Ceedling&#xff08;一个针对 C 代码单元测试的构建管理器&#xff09;&#xff0c;可以更方便地管理测试。Ceedling 会自动处理 Unity 和 CMock 的集成&#xff0c;无需手动编写 Makefile。 1.环境搭建 1.1 Ruby环境 sudo apt-get install ruby1.2 安…...

一文深入了解DeepSeek-R1:模型架构

本文深入探讨了 DeepSeek-R1 模型架构。让我们从输入到输出追踪 DeepSeek-R1 模型&#xff0c;以找到架构中的新发展和关键部分。DeepSeek-R1 基于 DeepSeek-V3-Base 模型架构。本文旨在涵盖其设计的所有重要方面。 &#x1f4dd; 1. 输入上下文长度 DeepSeek-R1的输入上下文长…...

机试题——快乐时间

题目描述 小明在工作之余喜欢在电子书城阅读不同的书籍并且获得最大的满足感&#xff0c;因此根据书城针对每本书籍的评分收集了 n 个书籍的打分清单 books&#xff0c;例如第一本书的打分 books[0]5 代表该书的满意程度为 5&#xff0c;第二本书 books[1]-2 代表该书的满意程…...

2024年终总结和2025年规划

2024年的主线是AI基础的学习和读书&#xff0c;虽然AI学习花费了更多的时间&#xff0c;但是读书长久看来于我是更重要的事情&#xff0c;哈哈哈&#xff0c;因此先简单回顾一下读书记忆&#xff0c;回顾我的2024&#xff0c;再展望一下我的2025. 我的2024年记忆 读万卷书&am…...

5 .TCP传输 文件/数据

文件传输 本质:客户端通过标准IO或者文件IO&#xff0c;读取文件中的信息 然后将读取到的信息&#xff0c;通过套接字发送给服务器 服务器接收到后&#xff0c;立刻通过标准IO或者文件IO写到文件 这个过程&#xff0c;服务器要知道2件事 1&#xff1a;客户端发来的文件名字 …...

哈希表(典型算法思想)—— OJ例题算法解析思路

目录 一、1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a; 1. 问题描述 2. 核心思路 3. 代码实现思路 &#xff08;1&#xff09;初始化哈希表 &#xff08;2&#xff09;遍历数组 &#xff08;3&#xff09;返回结果 4. 时间复杂度分析 …...

CloudberryDB(七)二级索引

在CloudberryDB中&#xff0c;二级索引的概念与PostgreSQL中的类似。但是&#xff0c;由于分布式特性&#xff0c;创建和使用二级索引需要考虑一些额外的因素。以下是关于二级索引的一些要点&#xff1a; 1. **创建索引**&#xff1a;在Greenplum中&#xff0c;可以使用CREATE…...

学习web数据埋点

什么是埋点&#xff0c;以及为什么需要埋点 通过代码主动收集用户行为数据&#xff08;如点击、浏览、停留时长等&#xff09;&#xff0c;用于数据分析驱动产品优化。 一、前端埋点 在客户端&#xff08;浏览器、移动端应用&#xff09;直接采集用户行为数据&#xff0c;通…...

Next.js【详解】CSS 样式方案

全局样式 Global CSS 默认已创建&#xff0c;即 src\app\globals.css&#xff0c;可根据需要修改 默认在全局布局中导入 src\app\layout.tsx import "./globals.css";组件样式 CSS Modules 新建文件 src\app\test\styles.module.css .red {color: red;}导入目标页面…...

HCIA项目实践--RIP相关原理知识面试问题总结回答

9.4 RIP 9.4.1 补充概念 什么是邻居&#xff1f; 邻居指的是在网络拓扑结构中与某一节点&#xff08;如路由器&#xff09;直接相连的其他节点。它们之间可以直接进行通信和数据交互&#xff0c;能互相交换路由信息等&#xff0c;以实现网络中的数据转发和路径选择等功能。&am…...

无人机信号调制技术原理

一、调制技术的必要性 频谱搬移&#xff1a;将低频的基带信号搬移到高频的载波上&#xff0c;便于天线辐射和传播。 信道复用&#xff1a; 利用不同的载波频率或调制方式&#xff0c;实现多路信号同时传输&#xff0c;提高信道利用率。 抗干扰&#xff1a; 通过选择合适的调…...

Qt——连接MySQL数据库之编译数据库驱动的方法详细总结(各版本大同小异,看这一篇就够了)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》 《实用硬件方案设计》 《结构建模设…...

leetcode-495.提莫攻击

leetcode-495.提莫攻击 文章目录 leetcode-495.提莫攻击一.题目描述二.代码提交三.解释 一.题目描述 二.代码提交 #include <vector> using namespace std;int findPoisonedDuration(vector<int>& timeSeries, int duration) {int total 0;for (int i 0; i …...

计算机网络知识速记 :HTTP多个TCP连接的实现方式

计算机网络知识速记 &#xff1a;HTTP多个TCP连接的实现方式 在当今互联网高速发展的背景下&#xff0c; web 应用程序对性能的要求日益增加。在众多网络协议中&#xff0c;HTTP (超文本传输协议) 的性能优化显得尤为重要&#xff0c;尤其是在多个TCP连接的管理和实现上。 引…...

5、《Spring Boot自动配置黑魔法:原理深度剖析》

Spring Boot自动配置黑魔法&#xff1a;原理深度剖析 一、引言&#xff1a;为什么Spring Boot能“开箱即用”&#xff1f; Spring Boot的核心理念是**“约定优于配置”&#xff0c;开发者只需引入一个spring-boot-starter-web依赖&#xff0c;就能直接编写RESTful API&#xf…...

Django 创建表时 “__str__ ”方法的使用

在 Django 模型中&#xff0c;__str__ 方法是一个 Python 特殊方法&#xff08;也称为“魔术方法”&#xff09;&#xff0c;用于定义对象的字符串表示形式。它的作用是控制当对象被转换为字符串时&#xff0c;应该返回什么样的内容。 示例&#xff1a; 我在初学ModelForm时尝…...