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

这篇具有很好参考价值的文章主要介绍了【OI学习笔记】基础算法-前缀和与差分算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

板块:基础算法、线性优化
难度:较易
前置知识: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.本质与应用领域

对于数组:

1 2 3 4 5 6 7

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

1 3 6 10 15 21 28
1 1+2 3+3 6+4 10+5 15+6 21+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]
【OI学习笔记】基础算法-前缀和与差分算法
要求以 ( 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]
【OI学习笔记】基础算法-前缀和与差分算法

二、差分

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 的差分.例如:

a 1 2 3 4 5
b 1 1 1 1 1

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 数组进行二维前缀和的求取即为答案.文章来源地址https://www.toymoban.com/news/detail-477646.html

到了这里,关于【OI学习笔记】基础算法-前缀和与差分算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法基础学习笔记——④前缀和\差分\双指针\位运算

    ✨ 博主: 命运之光 ✨ 专栏: 算法基础学习 目录 ✨前缀和 ✨一维前缀和 🍓一维前缀和模板: ✨二维前缀和 🍓二位前缀和模板: 前言: 算法学习笔记记录日常分享,需要的看哈O(∩_∩)O,感谢大家的支持! 原i:a[1] a[2] a[3] …a[n] 前缀和:s[i]=a[1]+a[2]+…+a[i] s[0]=0(方便处理

    2024年02月08日
    浏览(48)
  • C/C++ 前缀和与差分

    个人主页:仍有未知等待探索_C语言疑难,数据结构,算法-CSDN博客 专题分栏:算法_仍有未知等待探索的博客-CSDN博客 目录 一、前言 1、什么是前缀和 2、什么是差分 3、优势 1.朴素做法: 2.用差分数组 二、代码实现 1、给一个数组去求其差分数组 2、给一个数组去求其前缀和数

    2024年02月03日
    浏览(38)
  • 【algorithm】认真讲解前缀和与差分 (图文搭配)

    🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 📣系列专栏:AcWing算法笔记 今天的月色好美 这里介绍以下前缀和算法以及差分算法,用来梳理自己所学到的算法知识。 从我的理解角度来讲:前缀和就是高中数学当中的数列的求和Sn,差分就是前缀和的逆运算,就是

    2024年01月21日
    浏览(41)
  • 算法基础之差分和前缀和

    结论:差分是前缀和的逆运算 举例 一维差分 二维差分 用在一维子区间里的数都增减一个固定值,把对于区间内每个数的操作转换为对于差分数组中的端点的操作,时间复杂度降为o(1)。 用在二维子矩阵里的数都增减一个固定值,把对于子矩阵的每个数的操作转换为对应差分

    2024年02月07日
    浏览(45)
  • C++基础算法前缀和和差分篇

    📟作者主页:慢热的陕西人 🌴专栏链接:C++算法 📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言 主要讲解了前缀和和差分算法 Ⅵ .Ⅰ前缀和 ① 一维前缀和 : ​ 就是构建一个新的数组 s ,用来存储另一个数组的和前 i 个数组元素的和。用公式表达就是: S [ i ] = a [ 0 ]

    2024年02月16日
    浏览(51)
  • 一文带你深入了解算法笔记中的前缀与差分(附源码)

    📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段, 因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力 ——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的 📖作者主页:热爱编程的

    2023年04月12日
    浏览(74)
  • 前缀和&差分算法(Python版)

    前缀和与差分是常用的区间优化方式,其中前缀和数组可以将区间查询的时间复杂度降为常数,而差分数组则可以将区间修改的时间复杂度降为常数。 前缀和知识点简单易理解,但出题难度较大,需要根据题意挖掘出潜在的前缀和关系,建立辅助数组求解问题。 (1)一维前

    2024年04月17日
    浏览(53)
  • 洛谷题单--算法[2-1] 前缀和、差分与离散化

    目录 0.铺垫学习:p1115最大子段和--前缀和+贪心+DP 1.p1719最大加权矩形--前缀和+贪心+DP+矩阵压缩 原题链接: P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 原题: 题目描述 给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。 输入格式 第

    2024年02月22日
    浏览(38)
  • 【0基础学算法】前缀和 (超详细讲解+私人笔记+源码)

    目录 什么是前缀和 我们为什么要去学习前缀和 前缀和实现 如何求s[i]  S[i]的作用  模板 一维前缀和 二维前缀和 题目 第一题 第二题 哈喽大家好,很长时间忘记更新咱们的学算法系列文章了,今天我们终于迎来了我们零基础学算法的第四期内容,那就是前缀和,前缀和其实

    2024年02月05日
    浏览(65)
  • 《横向联邦学习中 PCA差分隐私数据发布算法》论文算法原理笔记

    论文地址:https://www.arocmag.com/article/01-2022-01-041.html 论文摘要      为了让不同组织在保护本地敏感数据和降维后发布数据隐私的前提下,联合使用 PCA进行降维和数据发布,提出 横向联邦 PCA差分隐私数据发布算法 。引入随机种子联合协商方案,在各站点之间以较少通信代

    2024年02月08日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包