Acwing算法基础【1】基础(三)前缀和与差分
目录
三、前缀和与差分
3.1 前缀和
3.1.1 一维前缀和的算法思想
3.1.2 一维前缀和的代码实现
3.1.3 二维前缀和的基本思想
3.1.4 二维前缀和的代码实现
3.2 差分
3.2.1 基本思想
3.2.2 代码实现
3.2.3 二维差分的基本思想
3.2.4 二维差分的代码实现
三、前缀和与差分
前缀和与差分是一堆逆运算
3.1 前缀和
3.1.1 一维前缀和的算法思想
原数组:一个长度为n的数组{a1, a2, a3 … , an}
前缀和数组:Si = a1+a2+…+ai,S0=0
(第一个下标一定要是1,把S0定义成0,这两个都是为了方便处理边界)
1、前缀和的作用:
能够快速地求出来原数组中一段数据的和,例如,如果想算一个数组中[l,r]的 数组的和,没有前缀和数组的话,复杂度就是O(n)的 ,如果有的话,我们就是可以直接 用 Sr - Sl-1 得到,查询的复杂度就是O(1), 但是求一个前缀和数组的复杂度就是O(n),适用于输入一组数据,让求好几段的数据的和的情况,
2、如何求Si?
( 可以用for循环 递推遍历一遍,S[i] = S[i-1]+ai)
3.1.2 一维前缀和的代码实现
#include <iostream>
using namespace std;const int N = 100010;int q[N];
int s[N];int main(){int n, m;scanf("%d %d", &n, &m);for(int i = 0; i < n; i++){scanf("%d", &q[i]);}s[0] = 0;// 构造前缀和数组for(int i = 1; i <= n;i++){s[i] = s[i-1] + q[i-1];}int l, r;while(m--){scanf("%d %d", &l, &r);printf("%d\n", s[r]-s[l-1]); //区间相减}return 0;
}
3.1.3 二维前缀和的基本思想
二位前缀和,画个图 如下所示:
1、如何求某区间的和?
想要求x1, x2, x3, x4所围成的小矩形 的和,就可以用,Sx2y2 - Sx1y2 - Sx2y1 + Sx1y1
2、如何求一个Si j
可以用两层for循环进行遍历,S[i][j] = S[i-1][j] +S[i][j-1] -S[i-1][j-1]+aij
所以定义S0=0,然后第一数都从1开始,就可以避免边界问题
3.1.4 二维前缀和的代码实现
#include <iostream>
using namespace std;const int N= 1010;int q[N][N];int main(){int n, m, qn;scanf("%d %d %d", &n, &m, &qn);//一定要从1开始,不然边界问题好难处理for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){scanf("%d", &q[i][j]);}}int s[N][N];// 构建前缀和for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){s[i][j] =s[i-1][j] + s[i][j-1]-s[i-1][j-1]+q[i][j]; }}int xa, ya, xb, yb;//区间更新while(qn--){scanf("%d %d %d %d", &xa, &ya, &xb, &yb);printf("%d\n", s[xb][yb] - s[xa-1][yb] -s[xb][ya-1] +s[xa-1][ya-1]);}return 0;
}
3.2 差分
差分是前缀和的逆运算
3.2.1 基本思想
数组:一个长度为n的数组{a1, a2, a3 … , an}
构造 一个b 数组,{b1,b2,b3…bn}
使得 ai = b1+b2+… +bi
即 b1 = a1; b2= a2-a1; b3 = a3-a2, … , bn = an-an-1
b 就是a 的差分,a就是b 的前缀和
1、差分数组的作用:
也就是在O(n)的时间, 通过 b 差分数组 可以还原出他的 前缀和数组 a ,运用 差分的思想 可以很好的解决一类问题:
例如给定一个区间 [l, r] 让a数组,这里边所有的数都加上c,如果是用遍历,那就需要O(n)的复杂度
如果用差分来做,就可以做到O(1)原理如下:
因为 ai = b1+ b2 + …+ bi
所以只需要对 b[l] 做 +c 的操作,那 a[l] 往后的数都会 +c
又因为题目要求,只是 [l, r] 的区间,所以在对b[r+1] 做-c 的操作,相抵消,那 a[r+1] 之后的数就不会加上c了,
这样再用b数组求前缀和,还原出的a数组 就是最后的结果
所以只需要O(1)的时间就可以实现,以空间换时间
2、如何拥有差分数组呢(初始化)
其实不要考虑 差分数组 的构造,换个角度考虑从0开始用bn 生成an,这样an生成之后,b数组出来了,具体思路如下:
可以先想象a 全部都是0,那么b 也全部都是
然后怎么样变成{a1, a2, a3 … , an}呢
就是相当于 每个地方,进行了加上这个数的操作
就[1,1] 的区间+a1, [2, 2]的区间+a2 …[n, n]的区间+an
这样生成 an 和 完成题目的an 某区间+c 操作,就都可以用同样的代码段了。
3.2.2 代码实现
#include<iostream>using namespace std;const int N = 100010;int a[N],b[N];//在某区间插入某个数,以得到差分数组和前缀和数组
int insert( int l, int r, int c){b[l] = b[l]+c;b[r+1] = b[r+1] -c;
}int main(){int n, m;scanf("%d %d", &n, &m);int c;//假设本身数组a全是0, 那其差分数组也是全0//然后一个个给两个数组插入数据,例如在[1,1] 加上a1,调用insert函数//注意边界,最好从1开始,不然边界超级难处理for(int i = 1; i <= n; i++){scanf("%d", &c);a[i] = c;insert( i, i, c); //意思就是从i 到i这个区间上的数字 都加上c,}int l, r;//更新区间while(m--){scanf("%d %d %d", &l, &r, &c);insert( l, r, c);}//用更新后的b数组还原a数组for(int i = 1; i <= n; i++){a[i] = a[i-1] + b[i]; }//输出最终的数组for(int i = 1; i <= n; i++){printf("%d ", a[i]);}return 0;
}
3.2.3 二维差分的基本思想
思路和一维的是一样的,也不需要考虑直接构造差分数组,而是从0 进行初始化,用bn来生成an,这样b 和 a 数组就都是初始的状态,我们只需要考虑,如何更新:
1、如何更新二维差分数组
如图,在b数组对应的点进行+c 或者 -c的操作,会影响的整个a数组这个点往右及往下的区域都+c或者-c,所以如果只需要对a数组的 x1 x2 y1 y2 围成的矩形中进行+c操作,转化为对b点的操作就是
b[x1][y1] + c; b[x2][y1] - c; b[x1][y2]-c; b[x2][y2]+c
3.2.4 二维差分的代码实现
#include <iostream>using namespace std;const int N = 1010; //二维数组的时候,这里不要太大,满足要求就行int q[N][N], b[N][N];void insert(int xa, int ya, int xb, int yb, int c){b[xa][ya] += c;b[xa][yb+1] -= c;b[xb+1][ya] -=c;b[xb+1][yb+1] +=c;
}int main(){int n, m, qn;scanf("%d %d %d", &n, &m, &qn);//以0开始构建数组for(int i = 1; i<=n; i++){for(int j = 1; j<=m; j++){scanf("%d", &q[i][j]);insert(i,j,i,j,q[i][j]);}}int xa, ya, xb, yb, c;//更新区间while(qn--){scanf("%d %d %d %d %d", &xa, &ya, &xb, &yb, &c);insert( xa, ya, xb, yb, c);}//生成最后的q数组for(int i = 1; i<=n; i++){for(int j = 1; j<=m; j++){q[i][j] = q[i][j-1]+q[i-1][j]-q[i-1][j-1]+b[i][j];}}//输出最终结果for(int i = 1; i<=n; i++){for(int j = 1; j<=m; j++){printf("%d ", q[i][j]);}printf("\n");}return 0;
}
写在最后:
这小节所讲的前缀和与差分,更重要的是一种思想,要明白各自适用于解决什么样的问题,可以在后续做题过程中多总结。加油
Acwing算法基础【1】基础(三)前缀和与差分相关推荐
- [AcWing算法基础课] 一.基础算法
Algorithms + Data Structures = Programs. --Niklaus Wirth 本章包括排序.二分.高精度.前缀和与差分.双指针算法.位运算.离散化.区间合并等内容 ...
- c++算法基础必刷题目——前缀和与差分
文章目录 前缀和与差分算法: 1.校门外的树 2.值周 3.中位数图 4.激光炸弹 5.二分 6.货仓选址 前缀和与差分算法: 前缀和与差分算法主要是为了快速求出某个区间的和,例如有一个数组a[1 ...
- yxc_第一章 基础算法(二)_前缀和与差分
目录 一.一维前缀和 1.零散知识点 2.AcWing 795 二.二维前缀和 1.AcWing 796 三.一维差分 1.AcWing 797 四.二维差分 1.puts函数 2.AcWing 79 ...
- 算法基础(2) | 高精度、前缀和、差分
文章目录 一.高精度 1.1 高精度加法 1.2 高精度减法 1.3 高精度乘法 1.4 高精度除法 二.前缀和 2.1 一维前缀和 2.2 二前缀和 三.差分 3.1 一维差分 3.2 二维差分 四 ...
- Acwing算法基础课学习笔记
Acwing学习笔记 第一章 基础算法 快速排序 归并排序 二分查找 前缀和与差分 差分 位运算 离散化 第二章 数据结构 单链表 双链表 栈 队列 单调栈 单调队列 KMP算法 Trie 并查集 堆 ...
- AcWing 算法基础课第三节基础算法3 双指针、位运算、离散化、区间合并
1.该系列为ACWing中算法基础课,已购买正版,课程作者为yxc 2.y总培训真的是业界良心,大家有时间可以报一下 3.为啥写在这儿,问就是oneNote的内存不够了QAQ ACwing C++ 算 ...
- AcWing 算法基础课第一节基础算法1排序、二分
1.该系列为ACWing中算法基础课,已购买正版,课程作者为yxc 2.y总培训真的是业界良心,大家有时间可以报一下 3.为啥写在这儿,问就是oneNote的内存不够了QAQ ACwing C++ 算 ...
- python基础教程第三版豆瓣-数据结构与算法必读书单吐血整理推荐【附网盘链接】...
前言:技术书阅读方法论 一.速读一遍(最好在1~2天内完成) 人的大脑记忆力有限,在一天内快速看完一本书会在大脑里留下深刻印象,对于之后复习以及总结都会有特别好的作用. 对于每一章的知识,先阅读标题, ...
- acwing算法基础__提高__进阶_课
文章目录 1.排序 1.1. 快速排序--分治 1.2. 归并排序--分治 2.二分 2.2. 整数二分 2.3. 小数二分(浮点数二分) 3.高精度 4.前缀和与差分 5.双指针算法 6.位运算 7 ...
最新文章
- linux -- ./configure --prefix 命令
- Linux大文件切割命令split
- FTServer 1.1 发布,多语言全文搜索服务器
- 五十八、Vue中的计算属性,方法和侦听器
- GDI与OpenGL与DirectX之间的区别
- 岳云鹏:买128G手机仅112G可用!手机系统占用存储空间应厂商消化?
- Step ‘Publish JUnit test result report’ failed: No test report files were found问题解决
- 黑客攻击手段揭秘(转)
- python个人博客毕业设计开题报告
- (基础篇)用python实现打印节日贺卡
- 多卡聚合路由器和普通路由器的区别
- 关于“击败”团队目标的思考
- 【Linux 内核设计的艺术】从开机加电到执行 main 函数之前的过程
- U转串口时,鼠标乱动,解决办法
- 【苹果CMS技术教程】苹果CMSV10基础安装过程,如何拥有自己的视频网站
- 解决linux下.AppImage文件无法运行问题
- iOS 左上角的返回按钮的几种设置
- 金蝶8.0 的安装办法
- 读书百客:《郡斋雨中与诸文士燕集》赏析
- foss测试_FOSS – Java开发人员的最好朋友