前缀和
1.什么是前缀和
这里只介绍一维前缀和与二维前缀和
即假设有一数列{an} ,那么前缀和就可以理解为 a1+a2+a3+...+ai。而前缀和数组即为存储以a1为起点,ai为终点的前缀和的数组。
举个例子:
若
b1=a1
b2=a1+a2
b3=a1+a2+a3
...
bn=a1+a2+a3+...+an
即可得
b1=a1
b2=b1+a2
b3=b2+a3
...
bn=bn−1+an
则{bn}为{an}的前缀和数组。
2.为什么需要前缀和?前缀和的作用是什么?
前缀和是一种重要的预处理,能大大降低查询的时间复杂度。何以见得?
举个例子:
假设我们需要计算 a1~an的和,通常我们的实现方式是:用一个for循环来求。那如果我需求换了,我要算a7 ~ an的和呢?那还是需要另一个for循环来求,那需求又换了,我要计算a4 ~ an−4呢?如果有n个这样的需求,那我们就要写n个循环,时间复杂度就变高了。
结论:
所以我们通过前缀和,一次for循环把a1 ~ ai 的和都算出来存到一个数组b中。
若我们需要求al ~ ar 的和,只需要利用b[r]−b[l−1] 即可得到al ~ ar的和,这个计算操作的时间复杂度为O(1)的。
证明:
b[r]=a[1]+a[2]+...+a[l−1]+a[l]+a[l+1]+...+a[r]
b[l−1]=a[1]+a[2]+...+a[l−1]
所以 b[r]−b[l−1]=a[l]+...+a[r]
3.二维前缀和
之前讲的都是一维的前缀和,你可能没有感觉到提升了多大的效率,到了二维数组,你可以自己脑补一下不用前缀和处理有多慢了。
二维前缀和相对难理解一点点,重要的是搞清楚前缀和数组存的是什么。
若有二维数组anm如下
⎣⎡a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44⎦⎤
那么anm的前缀和数组表示的是什么意思呢?记bnm为anm的前缀和数组。
则b[i][j]表示的是以a[1][1]为左上角,以a[i][j]为右下角的矩形,矩形内所有元素的和。
举个例子
b[2][3]表示的就是下列所有元素的和
[a11a21a12a22a13a23]
类似的
b[4][2]表示的就是下列所有元素的和
⎣⎡a11a21a31a41a12a22a32a42⎦⎤
结论
若我们要求a[x1][y1]为左上角,a[x2][y2]为右下角的矩形内所有元素的和,
即b[x2][y2]−b[x2][y1−1]−b[x1−1][y2]+b[x1−1][y1−1]。
证明就自己画个图就好了,还是比较简单的。记忆起来也比较容易,下标为1的都减一。
差分
1.什么是差分
可以理解为前缀和的逆运算。怎么理解?直接看个例子吧。
设有一差分数组{an} ,其前缀和数组为{bn}
则差分数组
a1=b1
a2=b2−b1
a3=b3−b2
...
an=bn−bn−1
而前缀和数组
b1=a1
b2=a1+a2
b3=a1+a2+a3
...
bn=a1+a2+...+an
这下就能理解差分了吧。一定要弄明白前缀和再来看差分。
2.差分的作用
用于维护多次对序列的一个区间加上一个数,比如我要在a1~an−5 这一段上每一个元素加上c,不要再想着用多个循环了噢,类似于前缀和,多次循环过后时间复杂度会变高。
3.差分的使用
对于一维的差分而言,想要在某段区间{l,r}加上一个数c,只需要
a[l]+=c
a[r+1]−=c
即可。
证明:我们一定要理解,差分数组加上一个数后,对前缀和数组的影响!
假设a1加上一个c,则前缀和数组{bn}会变成如下
b1=a1+c
b2=a1+c+a2
b3=a1+c+a2+a3
...
bn=a1+c+a2+...+an
可以发现,b1 ~ bn都加上溜了一个c,那如果此时 a2 减去一个c呢?
b1=a1+c
b2=a1+c+a2−c=a1+a2
b3=a1+c+a2−c+a3=a1+a2+a3
...
bn=a1+a2+...+an
最后的结果就只有 b1 加了 c 而已。更多的例子可以由读者自行测试加深印象。
所以,若 ai 加了一个数 c ,那么对应的前缀和数组中 bi 以及它后面所有的数都会加上 c,同理,若aj 减去一个数 c ,那么对应的前缀和数组中 bj 以及它后面所有的数都会减去 c 。
4.二维差分
有了一维差分的基础,我们来看看二维差分。首先一定要记住二维前缀和的含义,这个对理解二维差分很关键。
如下,有一差分数组 {a44} ,它对应的前缀和数组为 {b44},即
⎣⎡a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44⎦⎤ 和 ⎣⎡b11b21b31b41b12b22b32b42b13b23b33b43b14b24b34b44⎦⎤
若此时 a22 加上一个数 c ,那么前缀和数组会变成什么样呢?答案如下
⎣⎡b11b21b31b41b12b22+cb32+cb42+cb13b23+cb33+cb43+cb14b24+cb34+cb44+c⎦⎤
那继续 a24 减去一个数 c,则
⎣⎡b11b21b31b41b12b22+cb32+cb42+cb13b23+cb33+cb43+cb14b24b34b44⎦⎤
继续 a42 减去一个数 c,则
⎣⎡b11b21b31b41b12b22+cb32+cb42b13b23+cb33+cb43b14b24b34b44−c⎦⎤
最后 a44 加上一个数 c,即得
⎣⎡b11b21b31b41b12b22+cb32+cb42b13b23+cb33+cb43b14b24b34b44⎦⎤
而差分数组此时为
⎣⎡a11a21a31a41a12a22+ca32a42−ca13a23a33a43a14a24−ca34a44+c⎦⎤
如此我们可以看出,要想给以 ax1y1 为左上角,以 ax2y2 为右下角的小矩形中所有元素加上一个数 c,则需要
a[x1][y1]+=c
a[x1][y2+1]−=c
a[x2+1][y1]−=c
a[x2+1][y2+1]+=c
更多Java内容欢迎扫码关注我的公众号ACJavaBear,文章第一时间会发在上面。一起学Java吧
