604 字
3 分钟
学习笔记-4-前缀和&差分
前缀和
定义
定义很简单,就是一个数列中前n项的和。虽然简单,却是一种非常好用的减少时间复杂度的优化方法。
一维
原理
实现
定义
int sum[N];sum[0]=a[0];for(int i=;i<N;i++){ sum[i]=sum[i-1]+a[i];}
查询
//l到r区间之和cout<<sum[r]-sum[l-1];
二维
通过图片可知,sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]
不难推出,s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1, y1 - 1]
差分
差分可以理解为前缀和的逆运算,即如果一个数组a的前缀和数组为b,则数组a是数组b的差分数组。
构造差分
最简单的构造方法:
int a[n],[n];b[0]=a[0];for(int i=1;i<n;i++){ b[i]=a[i]-a[i-1];}
一维
一维差分是指给定一个长度为n的序列a,要求支持操作fuc(l,r,c)表示对a[l]~a[r]区间上的每一个值都加上或减去常数c,并求修改后的序列a。
//区间[l,r]中的所有值都加上常数cb[l] += c;b[r+1] -= c;
//上边语句实现原理 b相当于a的辅助数组//把a序列分为[1,l-1],[l,r],[r+1,n]三部分,由差分定义和与前缀和关系可得a[l-1] = b[1]+b[2]+...+b[l-1]; //b[1]~b[l-1]中所有值都未改变,a[l-1]也不变a[l] = b[1]+b[2]+...+b[l-1]+b[l]; //b[1] += c,所以a[l] += ca[l+1] = b[1]+b[2]+...+b[l-1]+b[l]+b[l+1]; //b[1] += c,所以a[l+1] += c... //一直到a[r] = b[1]+b[2]+...b[l]+...+b[r]; //b[1] += c,所以a[l+1] += ca[r+1] = b[1]+b[2]+...b[l]+...+b[r]+b[r+1]; //b[l] += c,b[r+1] -= c;所以a[r+1]不变
//所以由此可知上面的两个语句(b[l] += c;b[r+1] -= c)可以实现a数组在区间[l,r]内的所有值都加上了常数c
二维
如果扩展到二维,我们需要让二维数组被选中的子矩阵中的每个元素的值加上c,是否也可以达到O(1)的时间复杂度。答案是可以的,考虑二维差分。
操作
void insert(int x1,int y1,int x2,int y2,int c){ b[x1][y1] += c; b[x2+1][y1] -= c; b[x1][y2+1] -= c; b[x2+1][y2+1] += c;}
初始化
for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++) { insert(i,j,i,j,a[i][j]); }}
区间修改
对于区间 ,所有元素都加上一个值 ,等价于在差分数组中 。 然后对这个差分数组求前缀和即可找到答案。
此文章参考了前缀和与差分 图文并茂 超详细整理(全网最通俗易懂) 和算法笔记(六):差分法的相关内容,在此表示感谢。