通俗易懂的C++前缀和与差分算法图文示例详解

news2026/5/19 11:24:51
1、前缀和前缀和是指某序列的前n项和可以把它理解为数学上的数列的前n项和而差分可以看成前缀和的逆运算。合理的使用前缀和与差分可以将某些复杂的问题简单化。2、前缀和算法有什么好处先来了解这样一个问题输入一个长度为n的整数序列。接下来再输入m个询问每个询问输入一对l, r。对于每个询问输出原序列中从第l个数到第r个数的和。我们很容易想出暴力解法遍历区间求和。代码如下1234567891011121314intn,m;scanf(%d%d,n,m);for(inti1;in;i)scanf(%d,a[i]);while(m--){intl,r;intsum0;scanf(%d%d,l,r);for(intil;ir;i){suma[i];}printf(%d\n,sum);}这样的时间复杂度为O(n*m)如果n和m的数据量稍微大一点就有可能超时而我们如果使用前缀和的方法来做的话就能够将时间复杂度降到O(nm),大大提高了运算效率。具体做法首先做一个预处理定义一个sum[]数组sum[i]代表a数组中前i个数的和。求前缀和运算123456constintN1e510;intsum[N],a[N];//sum[i]a[1]a[2]a[3].....a[i];for(inti1;in;i){sum[i]sum[i-1]a[i];}然后查询操作12scanf(%d%d,l,r);printf(%d\n, sum[r]-sum[l-1]);对于每次查询只需执行sum[r]-sum[l-1]时间复杂度为O(1)原理sum[r] a[1]a[2]a[3]a[l-1]a[l]a[l1]......a[r];sum[l-1]a[1]a[2]a[3]a[l-1];sum[r]-sum[l-1]a[l]a[l1]......a[r];图解这样对于每个询问只需要执行sum[r]-sum[l-1]。输出原序列中从第l个数到第r个数的和的时间复杂度变成了O(1)。我们把它叫做一维前缀和。总结练习一道题目输入一个长度为n的整数序列。接下来再输入m个询问每个询问输入一对l, r。对于每个询问输出原序列中从第l个数到第r个数的和。输入格式第一行包含两个整数n和m。第二行包含n个整数表示整数数列。接下来m行每行包含两个整数l和r表示一个询问的区间范围。输出格式共m行每行输出一个询问的结果。数据范围1≤l≤r≤n,1≤n,m≤100000,−1000≤数列中元素的值≤1000输入样例5 32 1 3 6 41 21 32 4输出样例3610代码123456789101112131415161718#include iostreamusingnamespacestd;constintN 100010;intn, m;inta[N], s[N];intmain(){scanf(%d%d, n, m);for(inti 1; i n; i )scanf(%d, a[i]);for(inti 1; i n; i ) s[i] s[i - 1] a[i];// 前缀和的初始化while(m -- ){intl, r;scanf(%d%d, l, r);printf(%d\n, s[r] - s[l - 1]);// 区间和的计算}return0;}3、二维前缀和如果数组变成了二维数组怎么办呢先给出问题输入一个n行m列的整数矩阵再输入q个询问每个询问包含四个整数x1, y1, x2, y2表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。同一维前缀和一样我们先来定义一个二维数组s[][],s[i][j]表示二维数组中左上角(1,1)到右下角( i,j )所包围的矩阵元素的和。接下来推导二维前缀和的公式。先看一张图紫色面积是指(1,1)左上角到(i,j-1)右下角的矩形面积, 绿色面积是指(1,1)左上角到(i-1, 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 ] a[i] [j] - s[i-1][ j-1]接下来回归问题去求以(x1,y1)为左上角和以(x2,y2)为右下角的矩阵的元素的和。如图紫色面积是指( 1,1 )左上角到(x1-1,y2)右下角的矩形面积 黄色面积是指(1,1)左上角到(x2,y1-1)右下角的矩形面积不难推出绿色矩形的面积 整个外围面积s[x2, y2]- 黄色面积s[x2, y1 - 1]- 紫色面积s[x1 - 1, y2] 重复减去的红色面积s[x1 - 1, y1 - 1]因此二维前缀和的结论为以(x1, y1)为左上角(x2, y2)为右下角的子矩阵的和为s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] s[x1 - 1, y1 - 1]总结练习一道完整题目输入一个n行m列的整数矩阵再输入q个询问每个询问包含四个整数x1, y1, x2, y2表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。输入格式第一行包含三个整数nmq。接下来n行每行包含m个整数表示整数矩阵。接下来q行每行包含四个整数x1, y1, x2, y2表示一组询问。输出格式共q行每行输出一个询问的结果。数据范围1≤n,m≤1000,1≤q≤200000,1≤x1≤x2≤n,1≤y1≤y2≤m,−1000≤矩阵内元素的值≤1000输入样例3 4 31 7 2 43 6 2 82 1 2 31 1 2 22 1 3 41 3 3 4输出样例172721代码1234567891011121314151617181920212223#includeiostream#includecstdiousingnamespacestd;constintN1010;inta[N][N],s[N][N];intmain(){intn,m,q;scanf(%d%d%d,n,m,q);for(inti1;in;i)for(intj1;jm;j)scanf(%d,a[i][j]);for(inti1;in;i)for(intj1;jm;j)s[i][j]s[i-1][j]s[i][j-1]a[i][j]-s[i-1][j-1];while(q--){intx1,y1,x2,y2;scanf(%d%d%d%d,x1,y1,x2,y2);printf(%d\n,s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]s[x1-1][y1-1]);}return0;}4、差分5、一维差分类似于数学中的求导和积分差分可以看成前缀和的逆运算。差分数组首先给定一个原数组aa[1], a[2], a[3],,,,,, a[n];然后我们构造一个数组b b[1] ,b[2] , b[3],,,,,, b[i];使得 a[i] b[1] b[2 ] b[3] ,,,,,, b[i]也就是说a数组是b数组的前缀和数组反过来我们把b数组叫做a数组的差分数组。换句话说每一个a[i]都是b数组中从头开始的一段区间和。考虑如何构造差分b数组最为直接的方法如下a[0 ] 0;b[1] a[1] - a[0];b[2] a[2] - a[1];b[3] a [3] - a[2];........b[n] a[n] - a[n-1];图示:我们只要有b数组通过前缀和运算就可以在O(n)的时间内得到a数组 。知道了差分数组有什么用呢 别着急慢慢往下看。话说有这么一个问题给定区间[l ,r ]让我们把a数组中的[ l, r]区间中的每一个数都加上c,即 a[l] c , a[l1] c , a[l2] c ,,,,,, a[r] c;暴力做法是for循环l到r区间时间复杂度O(n)如果我们需要对原数组执行m次这样的操作时间复杂度就会变成O(n*m)。有没有更高效的做法吗? 考虑差分做法(差分数组派上用场了)。始终要记得a数组是b数组的前缀和数组比如对b数组的b[i]的修改会影响到a数组中从a[i]及往后的每一个数。首先让差分b数组中的b[l] c,通过前缀和运算a数组变成 a[l] c ,a[l1] c,,,,,, a[n] c;然后我们打个补丁b[r1] - c, 通过前缀和运算a数组变成 a[r1] - c,a[r2] - c,,,,,,,a[n] - c;为啥还要打个补丁我们画个图理解一下这个公式的由来:b[l] c效果使得a数组中a[l]及以后的数都加上了c(红色部分)但我们只要求l到r区间加上c, 因此还需要执行b[r1] - c,让a数组中a[r1]及往后的区间再减去c(绿色部分)这样对于a[r]以后区间的数相当于没有发生改变。因此我们得出一维差分结论给a数组中的[ l, r]区间中的每一个数都加上c,只需对差分数组b做b[l] c,b[r1] - c。时间复杂度为O(1), 大大提高了效率。总结题目练习 AcWing 797. 差分输入一个长度为n的整数序列。接下来输入m个操作每个操作包含三个整数l, r, c表示将序列中[l, r]之间的每个数加上c。请你输出进行完所有操作后的序列。输入格式第一行包含两个整数n和m。第二行包含n个整数表示整数序列。接下来m行每行包含三个整数lrc表示一个操作。输出格式共一行包含n个整数表示最终序列。数据范围1≤n,m≤100000,1≤l≤r≤n,−1000≤c≤1000,−1000≤整数序列中元素的值≤1000输入样例6 31 2 2 1 2 11 3 13 5 11 6 1输出样例3 4 5 3 4 2AC代码12345678910111213141516171819202122232425262728//差分 时间复杂度 o(m)#includeiostreamusingnamespacestd;constintN1e510;inta[N],b[N];intmain(){intn,m;scanf(%d%d,n,m);for(inti1;in;i){scanf(%d,a[i]);b[i]a[i]-a[i-1];//构建差分数组}intl,r,c;while(m--){scanf(%d%d%d,l,r,c);b[l]c;//表示将序列中[l, r]之间的每个数加上cb[r1]-c;}for(inti1;in;i){b[i]b[i-1];//求前缀和运算printf(%d ,b[i]);}return0;}6、二维差分如果扩展到二维我们需要让二维数组被选中的子矩阵中的每个元素的值加上c,是否也可以达到O(1)的时间复杂度。答案是可以的考虑二维差分。a[][]数组是b[][]数组的前缀和数组那么b[][]是a[][]的差分数组原数组a[i][j]我们去构造差分数组b[i][j]使得a数组中a[i][j]是b数组左上角(1,1)到右下角(i,j)所包围矩形元素的和。如何构造b数组呢其实关于差分数组我们并不用考虑其构造方法因为我们使用差分操作在对原数组进行修改的过程中实际上就可以构造出差分数组。同一维差分我们构造二维差分数组目的是为了 让原二维数组a中所选中子矩阵中的每一个元素加上c的操作可以由O(n*n)的时间复杂度优化成O(1)已知原数组a中被选中的子矩阵为 以(x1,y1)为左上角以(x2,y2)为右上角所围成的矩形区域;始终要记得a数组是b数组的前缀和数组比如对b数组的b[i][j]的修改会影响到a数组中从a[i][j]及往后的每一个数。假定我们已经构造好了b数组类比一维差分我们执行以下操作来使被选中的子矩阵中的每个元素的值加上cb[x1][y1] c;b[x1,][y21] - c;b[x21][y1] - c;b[x21][y21] c;每次对b数组执行以上操作等价于123for(intix1;ix2;i)for(intjy1;jy2;j)a[i][j]c;我们画个图去理解一下这个过程b[x1][ y1 ] c; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c。b[x1,][y21]-c; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c使其内元素不发生改变。b[x21][y1]- c; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c使其内元素不发生改变。b[x21][y21]c; 对应图4,让整个a数组中红色矩形面积的元素再加上c红色内的相当于被减了两次再加上一次c才能使其恢复。我们将上述操作封装成一个插入函数:1234567voidinsert(intx1,inty1,intx2,inty2,intc){//对b数组执行插入操作等价于对a数组中的(x1,y1)到(x2,y2)之间的元素都加上了cb[x1][y1]c;b[x21][y1]-c;b[x1][y21]-c;b[x21][y21]c;}我们可以先假想a数组为空那么b数组一开始也为空但是实际上a数组并不为空因此我们每次让以(i,j)为左上角到以(i,j)为右上角面积内元素(其实就是一个小方格的面积)去插入ca[i][j]等价于原数组a中(i,j)到(i,j)范围内 加上了a[i][j],因此执行n*m次插入操作就成功构建了差分b数组.这叫做曲线救国。代码如下1234567for(inti1;in;i){for(intj1;jm;j){insert(i,j,i,j,a[i][j]);//构建差分数组}}复制讲解当然关于二维差分操作也有直接的构造方法公式如下b[i][j]a[i][j]−a[i−1][j]−a[i][j−1]a[i−1][j−1]二维差分数组的构造同一维差分思维相同因次在这里就不再展开叙述了。总结题目练习 AcWing 798. 差分矩阵输入一个n行m列的整数矩阵再输入q个操作每个操作包含五个整数x1, y1, x2, y2, c其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上c。请你将进行完所有操作后的矩阵输出。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…