先来个小热身 ~~可看可不看~~(反正与本次无关)
如果要想在1S内解决问题:
O(n)的算法可以处理大约10^7级别的数据
O(n*logn)的算法可以处理大约10^6级别的数据
O(n^2)的算法可以处理大约10^4级别的数据
下面进入正题
什么是前缀和?
**前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和
我们设a[ ]是原数组,b[ ]为前缀和数组,根据这句话可得到前缀和的定义和递推式:
什么是差分?
**差分是一个数组相邻两元素的差,一般为下标靠后的减去靠前的一个
我们设差分数组p[ ],即: p[i]=a[i]-a[i-1];
差分是将数列中的每一项分别与前一项数做差
那么他们有什么性质呢?
- 差分序列求前缀和可得到原序列
- 将原序列区间[L,R]中的元素全部+1,可转化为差分序列L处+1和R+1处-1
- 按照性质2得到,每次修改原序列一个区间+1,那么每次差分序列修改处增加的和减少的相同
**没想到的是他们之间还有互逆的关系
令F(a)表示前缀和数组,G(a)表示差分数组 ,则F(G(a))=G(F(a))=a
好!正题来了!
咱先来讲一维前缀和(简单来说就是个公式)
用a表示原来数列,用s表示前缀和数列,某一项为i,则得到前i项的前缀和公式为:s[i]=a[1]+a[2]+a[3]+.....+a[i]
它的优点还是很明显的
1.能够求解一段区间内数字和是多少
2.时间快,O(1)的复杂度
****毕竟是区间操作探究,所以就来说一下区间前缀和的求解思路~~
比如说有个题让你求解[L,R]区间数字之和
常规做法呢就是从L到R循环一遍将区间内的数字累加起来,遍历一遍是o(n)的复杂度,但是对于m次区间和访问,时间复杂度就会变为o(m*n)
而用前缀和呢就能将时间复杂度降为o(m)
开个s数组,运用前缀和公式:s[L]=a[1]+a[2]+...a[L-1]+a[L];
s[L-1]=a[1]+a[2]+...a[L-1]; S[R]=a[1]+a[2]+...a[L-1]+a[L]+...a[R]; 因为L<R,所以显然[L,R]区间和为s[R]-s[L-1];
接着就是一维差分了
其实和一维前缀和差不多(毕竟是互逆的关系)
举个例子: 给定n=5个数的数组a[n]={1,3,7,5,2} //下标0开始
那么上面差分数组d[n]为={1,2,4,-2,-3} //原数组后-前:dn=ai-ai-1
差分数组d[n]前缀和为={1,3,7,5,2} //又得到原数组:差分前缀和互逆关系(没想到吧~~~~)
就此推出差分公式:[L,R]+V =d[L]+V,d[R+1]-V
m次操作后d[n]数组变为:d[n]={1,2+2,4+5,-2+3,-3-2}={-2,4,9,1,-5}
此时d[n]求前缀和为:sum_d[n]={-2,2,11,12,7}
最后做个小总结,差分公式(标记):
第一步 差分标记: [L,R]+V = d[L]+V,d[R+1]-V
第二步 求前缀和:把差分后的数组,进行一次前缀和操作
想必这些小儿科的东西没有什么问题了吧
那就来二维前缀和吧
首先,二维的前缀和s[][],如图a[2][3]的前缀和表示的是以下的值//下标从a[1][1]开始:
所以求前缀和,如图 s[2][3]=a[2][3]+s[1][3]+s[2][2]-s[1][2] 就是右图中a[2][3]+(蓝色值)+(紫色值)-(蓝色与紫色重复部分,加了2次[容斥原理])
最后可以得出二维前缀和公式: s[i][j]=a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]
那么二维前缀和区间范围求和又是什么样的呢?
如若要对区间范围求和,如下图区间
就可以得到如下图求a[x1][y1]->a[x2][y2]
结果=蓝色部分-黑色两个部分+紫色部分(重复减的)
ans=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1])
最后呢就是二维差分(区间差分操作)了
依旧是举例子:如果我们要在左上角是 (x1,y1),右下角是 (x2,y2) 的矩形区间每个值都 +p,下图所示 在区间开始位置(x1,y1)处 +p,根据前缀和的性质,那么它影响的就是整个黄色部分(所有的求和都增加了),多影响了两个蓝色部分。所以在两个蓝色部分 -p 消除 +p 的影响,而两个蓝色部分重叠的绿色部分多了个 -p 的影响,所以绿色部分 +p 消除影响。所以对应的计算方法如下:
****原理剖析:构建差分数据-->区间修改操作-->构建前缀和数组-->运用差分<=>前缀和操作互逆
这个地方呢需要感性理解一下,多体会其中的原理
相信你们绝对没有问题
OK!完结撒花!