【OI学习笔记】基础算法-前缀和与差分算法

news2024/11/26 3:55:47

板块:基础算法、线性优化
难度:较易
前置知识:C++基础语法

一、前缀和

1、定义

  • 在一维空间中,对于一个数据总量为 n n n 的数组 a a a,有数据 a [ 1 ] , a [ 2 ] , a [ 3 ] , . . . , a [ n − 1 ] , a [ n ] a[1],a[2],a[3],...,a[n-1],a[n] a[1],a[2],a[3],...,a[n1],a[n],定义一个数组 s u m sum sum s u m [ i ] sum[i] sum[i] 存储 a [ 1 ] ∼ a [ i ] a[1] \sim a[i] a[1]a[i] 的数据总和,我们就称 s u m sum sum a a a 的前缀和.

2.本质与应用领域

对于数组:

1234567

根据定义,我们计算出它各项所对应的前缀和应该是:

13610152128
11+23+36+410+515+621+7

不难发现,前缀和的本质就是 递推.从前往后依次计算出前缀和.前缀和是一种思想,它是一种对算法进行时间优化的预处理数据的思想.前缀和的应用领域很宽广,比如用于线性优化、求和、预处理数据等.

3.一维前缀和

上文中我们所举的数组便是典型的一维前缀和,其 预处理递推公式 为: f [ n ] = f [ n − 1 ] + a [ n ] ( n ≥ 1 , f [ 0 ] = 0 ) f[n]=f[n-1]+a[n](n \geq1,f[0]=0) f[n]=f[n1]+a[n](n1,f[0]=0)
易得其时间复杂度为 O ( n ) O(n) O(n).
完成预处理后,我们将会面临 查询 的问题,对于一个区间 l ∼ r l\sim r lr,查询其区间前缀和的公式为:
s [ r ] − s [ l − 1 ] ( l ≥ 1 ) s[r]-s[l-1](l\geq 1) s[r]s[l1](l1)
易得其时间复杂度为 O ( 1 ) O(1) O(1).

4.二维前缀和

二维前缀和和一维前缀和同理,需要推导出预处理递推公式和查询公式.
如下图,我们需要递推 a [ 1 ] [ 1 ] ∼ a [ i ] [ j ] a[1][1]\sim a[i][j] a[1][1]a[i][j] 的前缀和即 s [ i ] [ j ] s[i][j] s[i][j] .由图示可知, s [ i ] [ j − 1 ] s[i][j-1] s[i][j1] 存储的是 a [ 1 ] [ 1 ] ∼ a [ i ] [ j − 1 ] a[1][1]\sim a[i][j-1] a[1][1]a[i][j1] 的前缀和,即黄色区域和红色区域; s [ i − 1 ] [ j ] s[i-1][j] s[i1][j] 存储的是 a [ 1 ] [ 1 ] ∼ a [ i − 1 ] [ j ] a[1][1]\sim a[i-1][j] a[1][1]a[i1][j] 的前缀和,即黄色区域和红色区域.这个推导类似于在电脑中进行框选操作时拖动鼠标的而显示出的矩形区域.如果我们将 s [ i ] [ j − 1 ] s[i][j-1] s[i][j1] s [ i − 1 ] [ j ] s[i-1][j] s[i1][j] 相加,我们不难得到绿色区域、红色区域和两个黄色区域的前缀和,也就发现多出了一个黄色区域,因此减去 s [ i − 1 ] [ j − 1 ] s[i-1][j-1] s[i1][j1] 即可,然后再加上 a [ i ] [ j ] a[i][j] a[i][j],也就得到了 a [ 1 ] [ 1 ] ∼ a [ i ] [ j ] a[1][1]\sim a[i][j] a[1][1]a[i][j] 的前缀和.综上分析,我们可以得到二维前缀和的 预处理递推公式 为:
s [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j] s[i][j]=s[i1][j]+s[i][j1]s[i1][j1]+a[i][j]
二维前缀和递推公式图解
要求以 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 为左上端点, ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 为右下端点的子矩阵的和,我们可以从推导预处理公式的过程中得到启发. s [ x 1 − 1 ] [ y 2 ] s[x_1-1][y_2] s[x11][y2] 存储的是 a [ 1 ] [ 1 ] ∼ a [ x 1 − 1 ] [ y 2 ] a[1][1]\sim a[x_1-1][y_2] a[1][1]a[x11][y2] 的前缀和,即红色区域和绿色区域; s [ x 2 ] [ y 1 − 1 ] s[x_2][y_1-1] s[x2][y11] 存储的是 a [ 1 ] [ 1 ] ∼ a [ x 2 ] [ y 1 − 1 ] a[1][1]\sim a[x_2][y_1-1] a[1][1]a[x2][y11] 的前缀和,即红色区域和黄色区域.借鉴推导一维前缀和查询公式时的思路,如果我们用 s [ x 2 ] [ y 2 ] s[x_2][y_2] s[x2][y2] 减去 s [ x 1 − 1 ] [ y 2 ] s[x_1-1][y_2] s[x11][y2] s [ x 2 ] [ y 1 − 1 ] s[x_2][y_1-1] s[x2][y11] 的和,再加上多减的一个红色区域也就是 s [ x 1 − 1 ] [ y 1 − 1 ] s[x_1-1][y_1-1] s[x11][y11],就可以得到 s [ x 1 ] [ y 1 ] ∼ s [ x 2 ] [ y 2 ] s[x_1][y_1]\sim s[x_2][y_2] s[x1][y1]s[x2][y2] 的总和.综上分析,可以得到二维前缀和的查询公式是:
s [ x 2 ] [ y 2 ] − s [ x 2 ] [ y 1 − 1 ] − s [ x 1 − 1 ] [ y 2 ] + s [ x 1 ] [ y 1 ] s[x_2][y_2]-s[x_2][y_1-1]-s[x_1-1][y2]+s[x_1][y_1] s[x2][y2]s[x2][y11]s[x11][y2]+s[x1][y1]
二维前缀和查询公式

二、差分

1.定义

在一维空间中,对于一个数据总量为 n n n 的数组 a a a,有数据 a [ 1 ] , a [ 2 ] , a [ 3 ] , . . . , a [ n ] a[1],a[2],a[3],...,a[n] a[1],a[2],a[3],...,a[n],构造一个数组 b b b,有数据为 b [ 1 ] , b [ 2 ] , b [ 3 ] , . . . , b [ n ] b[1],b[2],b[3],...,b[n] b[1],b[2],b[3],...,b[n],使得 a [ i ] a[i] a[i] b [ 1 ] ∼ b [ i ] b[1]\sim b[i] b[1]b[i] 的前缀和,我们就称 b b b a a a 的差分.例如:

a12345
b11111

2.实质

通过定义,我们可以知道:我们要想求 b [ i ] b[i] b[i],那么就是计算 a [ i ] − a [ i − 1 ] a[i]-a[i-1] a[i]a[i1],因为 b b b a a a 的差分, a a a b b b 的前缀和.由此我们可以知道,差分是前缀和的逆运算.

3.一维差分

以 AcWing797.差分 一题为例,如果我们想要让 a [ l ] ∼ a [ r ] a[l] \sim a[r] a[l]a[r] 都加上 c c c,可以采用循环的方式,但是这样的话,其时间复杂度为 O ( n m ) O(nm) O(nm).

如何优化呢?借鉴前缀和的思想,对于区间操作,我们可以让 a [ l ] a[l] a[l] 加上 c c c,按照前缀和的思维,让 a [ l ] ∼ a [ n ] a[l]\sim a[n] a[l]a[n] 都加上了 c c c;再让 a [ r + 1 ] a[r+1] a[r+1] 减去 c c c,按照前缀和的思维 a [ r + 1 ] ∼ a [ n ] a[r+1]\sim a[n] a[r+1]a[n] 都减去了 c c c,这样的话,就满足了题目要求.

但问题来了,显然在上述分析中,我们只是设想利用前缀和思维,但实际上我们显然不能够直接对 a a a 数组进行这样的操作再求前缀和,这样的话我们得到的就是 原数组的前缀和数组,而不是原始数组操作后的结果.为了解决这样的问题,差分 就派上用场了.我们可以先构造原数组 a a a差分数组 b b b,先对 b b b 进行上文中的操作,即若需要让 a [ l ] ∼ a [ r ] a[l]\sim a[r] a[l]a[r] 都加上 c c c,就先让 b [ l ] b[l] b[l] 加上 c c c,再让 b [ r + 1 ] b[r+1] b[r+1] 减去 c c c,最后对 b b b 求取前缀和,就得到了 原数组 a a a 操作后的数组,且保证了是在原数组上进行的操作.这就体现了差分是前缀和的逆运算、对原数组的差分数组求取前缀和就是原数组.

综上,解决原题的关键操作代码就是:

void insert(int l, int r, int c) {
    b[l] += c;
    b[r + 1] -= c;
}

但还有一个问题,在执行插入操作前如何构造差分数组呢?根据前文中差分数组的定义和实质分析,构造原数组的差分数组,就是让 b [ i ] b[i] b[i] 加上 a [ i ] a[i] a[i], 让 b [ i + 1 ] b[i+1] b[i+1] 减去 a [ i ] a[i] a[i],就构造了 a a a 的差分数组 b b b;当然也可以直接使用公式 b [ i ] = a [ i ] − a [ i − 1 ] b[i]=a[i]-a[i-1] b[i]=a[i]a[i1].

最后对 b b b 数组求取前缀和,得到答案.

4.二维差分

二维差分可以类比于一维差分,即对一个前缀和的差分.以AcWing.797 二维差分为例.给定以 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 为左上端点,以 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 为右下端点的子矩阵,将 a a a 所对应的差分数组 b b b进行如下操作:将 b [ x 1 ] [ y 1 ] b[x_1][y_1] b[x1][y1] 加上 c c c,这样就使 a [ x 1 ] [ y 1 ] ∼ a [ n ] [ m ] a[x_1][y_1]\sim a[n][m] a[x1][y1]a[n][m] 都加上了 c c c,再迁移推导二维前缀和查询公式时的思维,将 b [ x 1 ] [ y 2 + 1 ] 、 b [ x 2 + 1 ] [ y 1 ] b[x_1][y_2+1]、b[x_2+1][y_1] b[x1][y2+1]b[x2+1][y1] 减去 c c c, 再对多减的 b [ x 2 + 1 ] [ y 2 + 1 ] b[x_2+1][y_2+1] b[x2+1][y2+1] 加上 c c c, 便完成了对于该子矩阵的操作.

同样的,我们也需要在操作前先构造差分数组,我们依然可以选用顺着一维差分的思维,选用公式或利用插入操作.

  • 公式: b [ i ] [ j ] = a [ i ] [ j ] − a [ i − 1 ] [ j ] − a [ i ] [ j − 1 ] + a [ i − 1 ] [ j − 1 ] b[i][j] = a[i][j] - a[i - 1][j]-a[i][j-1]+a[i-1][j-1] b[i][j]=a[i][j]a[i1][j]a[i][j1]+a[i1][j1]
  • 插入操作,按照上述对子矩阵的操作思路,进行如下操作:
insert(i, j, i, j, a[i][j]);

最后,再对 b b b 数组进行二维前缀和的求取即为答案.

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

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

相关文章

计算数组中各元素的平方根numpy.sqrt()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算数组中各元素的平方根 numpy.sqrt() 选择题 以下程序的运行结果是? import numpy as np a np.array([1, 4, 9, -4]) print("【显示】a ", a) print("【执行】np.sqrt(a)&…

计网常见面试题

参考:小林coding 1.TCP/IP模型 2.说一下TCP的三次握手? 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位SYN=1,序列号seq=x。第一次握手前客户端的状态为CLOSE,第一次握手后客户端的状态为SYN-SENT。…

Stable Diffusion 从听说到超神日记(从安装开始)

1.安装模型(B站搜秋叶) 看到下面界面就成功了! 下载好模型打开主页!点击右下角一键启动! 首先弹出下面命令界面。 自动弹出一个网页界面(地址是http://127.0.0.1:7860/?__themelight)&#xf…

Zabbix(二)

所有实验的zabbix_agent客户端都是web1(192.168.29.142) 模板Template 模板是可以方便地应用于多个主机的一组实体。 实体可以是: 监控项触发器图表仪表盘低级别自动发现规则web场景 模板通常用于为特定服务或应用程序(如Apache,MySQL&am…

第二章 模态命题:必然、可能

第二章 模态命题:必然、可能 第一节 模态命题-句式转换-逻辑转换 题-模态命题-句式转换-逻辑转换:①不一定不可能;②不一定可能不未必。 1.唐代韩愈在《师说》中指出:“孔子曰:三人行,则必有我师。是故…

MM32F3273G8P火龙果开发板MindSDK开发教程1 - 点亮LED

MM32F3273G8P火龙果开发版MindSDK开发教程1-点亮LED 1、登录官网下载对应的MindSDK固件 https://mindsdk.mindmotion.com.cn/,然后注册下载mm32F3270的固件即可。 下载完的文件为 plus-f3270_mdk.zip 解压后的文件路径如图: 2、新建LED工程 将下载…

ModNet抠图算法及摄像头实时抠图示例

目录 一、视频抠图采用绿幕的原因 1、摄像机成色原因 2、抠图效果原因 3、经济成本 二、抠图背景知识 1、Trimap 2、什么是抠图 3、抠图算法分类 三、Deep Image Matting算法 1、网络结构图 2、算法解读 (1)Encoder-Decoder阶段 &#xff0…

vue3 -- lottie-web使用

Lottie简介 官方介绍:Lottie是一个库,可以解析使用AE制作的动画(需要用bodymovie导出为json格式),支持web、ios、android、flutter和react native。在web端,lottie-web库可以解析导出的动画json文件,并将其以svg或者canvas的方式将动画绘制在我们的页面上. Lottie的优点 …

chatgpt赋能python:Python版本切换教程

Python版本切换教程 Python是一种高级编程语言,用于多种编程任务。但是,由于Python版本之间的不兼容性,有时候需要切换Python版本以满足特定的需求。在本文中,我们将介绍Python版本切换的方法,包括安装和使用多个版本…

机器学习 | 集成算法 | Bagging | Boosting | 概念向

📚Bagging和Boosting的概念 集成学习(Ensemble Learning)就是通过某种策略将多个模型集成起来,通过群体决策来提高决策准确率。为什么集成学习会好于单个学习器呢?原因可能有三: 训练样本可能无法选择出最好…

【ARMv8 SIMD和浮点指令编程】NEON 通用算术指令——杂项也不少

算术通用指令杂项包括以下指令: UABA、UABAL/UABAL2、UABD 和 UABDL/UABDL2。无符号向量差值绝对值累加和差值绝对值。 ABS 和 NEG向量绝对值和求反。 UMAX、UMIN、UPMAX、UPMIN、UMAXV 和 UMINV。无符号向量最大值,无符号向量最小值,无符号向量按对最大值,无符号向量按对最…

chatgpt赋能python:Python怎么分行输出?教程来了!

Python怎么分行输出?教程来了! Python是一种解释型、面向对象、动态数据类型的高级编程语言。在Python中,分行输出是非常常见的操作,本文将介绍Python分行输出的不同方式以及使用的情况。 一、使用换行符 使用换行符是Python分…

python文字转语音(pyttsx3+flask)

提示:文章结尾有全部代码 目录 前言一、Flaskpyttsx基本使用Flask导入Flask框架配置基础环境初始Flask代码 pyttsx3库基本使用导入pyttsx3初始化pyttsx3文字转语音运行 二、具体实现1.引入库 总结 前言 本文主要讲解如何用python的pyttsx3库flask框架,手…

chatgpt赋能python:Python切换指南:让你无缝转换到Python

Python切换指南:让你无缝转换到Python Python是一个高级的编程语言,它可以用来进行各种各样的应用开发和数据分析。 Python有很多优点,比如它易于学习和使用,它是一个开源的语言,它具有广泛的库和框架。 如果你是处于…

Mysql数据库(六):基本的SELECT语句

基本的SELECT语句 前言一、SELECT...二、SELECT ... FROM三、列的别名四、去除重复行五、空值参与运算六、着重号七、查询常数八、显示表结构九、过滤数据 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主&#…

如何监控EMC VNX控制器的启动过程

这里我们要讨论的内容基本上适用于所有的EMC VNX中端存储系统,包含老的Clariion CX3,CX4,VNX1和VNX2,其实VNXe和Unity很多内容也是一样的。当然由于VNXe和Unity 操作系统的大的变化,差异也是比较大的。 导致EMC Clarr…

什么是M-LAG?为什么需要M-LAG?

M-LAG(Multichassis Link Aggregation Group)提供一种跨设备链路聚合的技术。M-LAG通过将两台接入交换机以同一个状态和用户侧设备或服务器进行跨设备的链路聚合,把链路的可靠性从单板级提升到设备级。同时,由于M-LAG设备可以单独…

如何设计一个完整的交互流程,提升产品用户体验

交互流程设计是一项关乎用户体验的重要工作。通过设计和规划用户与产品或服务的交互方式和流程,我们可以提高用户的满意度和使用效果。在本文中,我们将深入探讨交互流程设计的关键要素以及其对用户体验的重要性。 交互流程设计本质是通过设计和规划用户与…

【Python】Python进阶系列教程-- Python3 SMTP发送邮件(六)

文章目录 前言实例使用Python发送HTML格式的邮件Python 发送带附件的邮件在 HTML 文本中添加图片使用第三方 SMTP 服务发送 前言 往期回顾: Python进阶系列教程-- Python3 正则表达式(一)Python进阶系列教程-- Python3 CGI编程(…

chatgpt赋能python:Python怎么倒序输出字符串

Python怎么倒序输出字符串 Python是一种高级编程语言,它可以让开发人员快速编写代码。在Python中,字符串是一种非常常见的数据类型,其支持各种字符串操作。在这篇文章中,我们将讨论如何在Python中倒序输出字符串。 倒序输出字符…