1.什么是前缀和
这里只介绍一维前缀和与二维前缀和
即假设有一数列{an}\{ a_n\}{an} ,那么前缀和就可以理解为 a1+a2+a3+...+aia_1 + a_2 + a_3 + ... + a_ia1+a2+a3+...+ai。而前缀和数组即为存储以a1a_1a1为起点,aia_iai为终点的前缀和的数组。
举个例子:
若
b1=a1b_1 = a_1b1=a1
b2=a1+a2b_2 = a_1 + a_2b2=a1+a2
b3=a1+a2+a3b_3 = a_1 + a_2 + a_3b3=a1+a2+a3
.........
bn=a1+a2+a3+...+anb_n = a_1 + a_2 + a_3 + ... + a_nbn=a1+a2+a3+...+an
即可得
b1=a1b_1 = a_1 b1=a1
b2=b1+a2b_2 = b_1 + a_2b2=b1+a2
b3=b2+a3b_3 = b_2+ a_3b3=b2+a3
.........
bn=bn−1+anb_n = b_{n-1}+ a_nbn=bn−1+an
则{bn}\{b_n\}{bn}为{an}\{a_n\}{an}的前缀和数组。
2.为什么需要前缀和?前缀和的作用是什么?
**前缀和是一种重要的预处理,能大大降低查询的时间复杂度。**何以见得?
举个例子:
假设我们需要计算 a1a_1a1~ana_nan的和,通常我们的实现方式是:用一个for循环来求。那如果我需求换了,我要算a7a_7a7 ~ ana_nan的和呢?那还是需要另一个for循环来求,那需求又换了,我要计算a4a_4a4 ~ an−4a_{n-4}an−4呢?如果有n个这样的需求,那我们就要写n个循环,时间复杂度就变高了。
结论:
所以我们通过前缀和,一次for循环把a1a_1a1 ~ aia_iai 的和都算出来存到一个数组b中。
若我们需要求ala_lal ~ ara_rar 的和,只需要利用b[r]−b[l−1]b[r] - b[l-1]b[r]−b[l−1] 即可得到ala_lal ~ ara_rar的和,这个计算操作的时间复杂度为O(1)O(1)O(1)的。
证明:
b[r]=a[1]+a[2]+...+a[l−1]+a[l]+a[l+1]+...+a[r]b[r] = a[1] + a[2] + ... + a[l-1] + a[l] + a[l+1] + ... + a[r]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[l-1] = a[1] + a[2] + ... + a[l-1]b[l−1]=a[1]+a[2]+...+a[l−1]
所以 b[r]−b[l−1]=a[l]+...+a[r]b[r] - b[l-1] = a[l] + ... + a[r]b[r]−b[l−1]=a[l]+...+a[r]
3.二维前缀和
之前讲的都是一维的前缀和,你可能没有感觉到提升了多大的效率,到了二维数组,你可以自己脑补一下不用前缀和处理有多慢了。
二维前缀和相对难理解一点点,重要的是搞清楚前缀和数组存的是什么。
若有二维数组anma_{nm}anm如下
[a11a12a13a14a21a22a23a24a31a32a33a34a41a42a43a44]\left[ \begin{matrix} a_{11} &a_{12} &a_{13} &a_{14}\\a_{21} &a_{22} &a_{23} &a_{24}\\a_{31} &a_{32} &a_{33} &a_{34}\\a_{41} &a_{42} &a_{43} &a_{44}\end{matrix} \right]⎣⎢⎢⎡a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44⎦⎥⎥⎤
那么anma_{nm}anm的前缀和数组表示的是什么意思呢?记bnmb_{nm}bnm为anma_{nm}anm的前缀和数组。
则b[i][j]b[i][j]b[i][j]表示的是以a[1][1]a[1][1]a[1][1]为左上角,以a[i][j]a[i][j]a[i][j]为右下角的矩形,矩形内所有元素的和。
举个例子
b[2][3]b[2][3]b[2][3]表示的就是下列所有元素的和
[a11a12a13a21a22a23]\left[\begin{matrix}a_{11} &a_{12} &a_{13}\\a_{21} &a_{22} &a_{23} \end{matrix}\right][a11a21a12a22a13a23]
类似的
b[4][2]b[4][2]b[4][2]表示的就是下列所有元素的和
[a11a12a21a22a31a32a41a42]\left[\begin{matrix} a_{11} &a_{12} \\a_{21} &a_{22} \\ a_{31} &a_{32}\\ a_{41} &a_{42}\end{matrix}\right]⎣⎢⎢⎡a11a21a31a41a12a22a32a42⎦⎥⎥⎤
结论
若我们要求a[x1][y1]a[x_1][y_1]a[x1][y1]为左上角,a[x2][y2]a[x_2][y_2]a[x2][y2]为右下角的矩形内所有元素的和,
即b[x2][y2]−b[x2][y1−1]−b[x1−1][y2]+b[x1−1][y1−1]。b[x_2][y_2] - b[x_2][y_1-1] - b[x_1-1][y_2] + b[x_1-1][y_1-1]。b[x2][y2]−b[x2][y1−1]−b[x1−1][y2]+b[x1−1][y1−1]。
证明就自己画个图就好了,还是比较简单的。记忆起来也比较容易,下标为1的都减一。
差分
1.什么是差分
可以理解为前缀和的逆运算。怎么理解?直接看个例子吧。
设有一差分数组{an}\{a_n\}{an} ,其前缀和数组为{bn}\{b_n\}{bn}
则差分数组
a1=b1a_1 = b_1a1=b1
a2=b2−b1a_2 = b_2 - b_1a2=b2−b1
a3=b3−b2a_3 = b_3 - b_2a3=b3−b2
.........
an=bn−bn−1a_n = b_n - b_{n-1}an=bn−bn−1
而前缀和数组
b1=a1b_1 = a_1b1=a1
b2=a1+a2b_2 = a_1 + a_2b2=a1+a2
b3=a1+a2+a3b_3 = a_1 + a_2 + a_3b3=a1+a2+a3
.........
bn=a1+a2+...+anb_n = a_1 + a_2 + ... + a_nbn=a1+a2+...+an
这下就能理解差分了吧。一定要弄明白前缀和再来看差分。
2.差分的作用
用于维护多次对序列的一个区间加上一个数,比如我要在a1a_1a1~an−5a_{n-5}an−5 这一段上每一个元素加上ccc,不要再想着用多个循环了噢,类似于前缀和,多次循环过后时间复杂度会变高。
3.差分的使用
对于一维的差分而言,想要在某段区间{l,r}\{l,r\}{l,r}加上一个数ccc,只需要
a[l]+=ca[l] += ca[l]+=c
a[r+1]−=ca[r+1] -= ca[r+1]−=c
即可。
证明:我们一定要理解,差分数组加上一个数后,对前缀和数组的影响!
假设a1a_1a1加上一个ccc,则前缀和数组{bn}\{b_n\}{bn}会变成如下
b1=a1+cb_1 = a_1 + cb1=a1+c
b2=a1+c+a2b_2 = a_1 + c + a_2b2=a1+c+a2
b3=a1+c+a2+a3b_3 = a_1 + c + a_2 + a_3b3=a1+c+a2+a3
.........
bn=a1+c+a2+...+anb_n = a_1 + c + a_2 + ... + a_nbn=a1+c+a2+...+an
可以发现,b1b_1b1 ~ bnb_nbn都加上溜了一个ccc,那如果此时 a2a_2a2 减去一个ccc呢?
b1=a1+cb_1 = a_1 + cb1=a1+c
b2=a1+c+a2−c=a1+a2b_2 = a_1 + c + a_2 - c = a_1 + a_2b2=a1+c+a2−c=a1+a2
b3=a1+c+a2−c+a3=a1+a2+a3b_3 = a_1 + c + a_2 - c + a_3 = a_1 + a_2 + a_3b3=a1+c+a2−c+a3=a1+a2+a3
.........
bn=a1+a2+...+anb_n = a_1 + a_2 + ... + a_nbn=a1+a2+...+an
最后的结果就只有 b1b_1b1 加了 ccc 而已。更多的例子可以由读者自行测试加深印象。
所以,若 aia_iai 加了一个数 ccc ,那么对应的前缀和数组中 bib_ibi 以及它后面所有的数都会加上 ccc,同理,若aja_jaj 减去一个数 ccc ,那么对应的前缀和数组中 bjb_jbj 以及它后面所有的数都会减去 ccc 。
4.二维差分
有了一维差分的基础,我们来看看二维差分。首先一定要记住二维前缀和的含义,这个对理解二维差分很关键。
如下,有一差分数组 {a44}\{a_{44}\}{a44} ,它对应的前缀和数组为 {b44}\{b_{44}\}{b44},即
[a11a12a13a14a21a22a23a24a31a32a33a34a41a42a43a44]\left[ \begin{matrix} a_{11} &a_{12} &a_{13} &a_{14}\\a_{21} &a_{22} &a_{23} &a_{24}\\a_{31} &a_{32} &a_{33} &a_{34}\\a_{41} &a_{42} &a_{43} &a_{44}\end{matrix} \right]⎣⎢⎢⎡a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44⎦⎥⎥⎤ 和 [b11b12b13b14b21b22b23b24b31b32b33b34b41b42b43b44]\left[ \begin{matrix} b_{11} &b_{12} &b_{13} &b_{14}\\b_{21} &b_{22} &b_{23} &b_{24}\\b_{31} &b_{32} &b_{33} &b_{34}\\b_{41} &b_{42} &b_{43} &b_{44}\end{matrix} \right]⎣⎢⎢⎡b11b21b31b41b12b22b32b42b13b23b33b43b14b24b34b44⎦⎥⎥⎤
若此时 a22a_{22}a22 加上一个数 ccc ,那么前缀和数组会变成什么样呢?答案如下
[b11b12b13b14b21b22+cb23+cb24+cb31b32+cb33+cb34+cb41b42+cb43+cb44+c]\left[ \begin{matrix} b_{11} &b_{12} &b_{13} &b_{14}\\b_{21} &b_{22} + c &b_{23} + c &b_{24} + c\\b_{31} &b_{32} + c &b_{33} + c &b_{34} + c\\b_{41} &b_{42} + c &b_{43} + c &b_{44} + c\end{matrix} \right]⎣⎢⎢⎡b11b21b31b41b12b22+cb32+cb42+cb13b23+cb33+cb43+cb14b24+cb34+cb44+c⎦⎥⎥⎤
那继续 a24a_{24}a24 减去一个数 ccc,则
[b11b12b13b14b21b22+cb23+cb24b31b32+cb33+cb34b41b42+cb43+cb44]\left[ \begin{matrix} b_{11} &b_{12} &b_{13} &b_{14}\\b_{21} &b_{22} + c &b_{23} + c &b_{24} \\b_{31} &b_{32} + c &b_{33} + c &b_{34}\\b_{41} &b_{42} + c &b_{43} + c &b_{44}\end{matrix} \right]⎣⎢⎢⎡b11b21b31b41b12b22+cb32+cb42+cb13b23+cb33+cb43+cb14b24b34b44⎦⎥⎥⎤
继续 a42a_{42}a42 减去一个数 ccc,则
[b11b12b13b14b21b22+cb23+cb24b31b32+cb33+cb34b41b42b43b44−c]\left[ \begin{matrix} b_{11} &b_{12} &b_{13} &b_{14}\\b_{21} &b_{22} + c &b_{23} + c &b_{24} \\b_{31} &b_{32} + c &b_{33} + c &b_{34}\\b_{41} &b_{42} &b_{43} &b_{44} - c\end{matrix} \right]⎣⎢⎢⎡b11b21b31b41b12b22+cb32+cb42b13b23+cb33+cb43b14b24b34b44−c⎦⎥⎥⎤
最后 a44a_{44}a44 加上一个数 ccc,即得
[b11b12b13b14b21b22+cb23+cb24b31b32+cb33+cb34b41b42b43b44]\left[ \begin{matrix} b_{11} &b_{12} &b_{13} &b_{14}\\b_{21} &b_{22} + c &b_{23} + c &b_{24} \\b_{31} &b_{32} + c &b_{33} + c &b_{34}\\b_{41} &b_{42} &b_{43} &b_{44}\end{matrix} \right]⎣⎢⎢⎡b11b21b31b41b12b22+cb32+cb42b13b23+cb33+cb43b14b24b34b44⎦⎥⎥⎤
而差分数组此时为
[a11a12a13a14a21a22+ca23a24−ca31a32a33a34a41a42−ca43a44+c]\left[ \begin{matrix} a_{11} &a_{12} &a_{13} &a_{14}\\a_{21} &a_{22} + c &a_{23} &a_{24} - c\\a_{31} &a_{32} &a_{33} &a_{34}\\a_{41} &a_{42} - c &a_{43} &a_{44} + c\end{matrix} \right]⎣⎢⎢⎡a11a21a31a41a12a22+ca32a42−ca13a23a33a43a14a24−ca34a44+c⎦⎥⎥⎤
如此我们可以看出,要想给以 ax1y1a_{x_1y_1}ax1y1 为左上角,以 ax2y2a_{x_2y_2}ax2y2 为右下角的小矩形中所有元素加上一个数 ccc,则需要
a[x1][y1]+=ca[x_1][y_1] += ca[x1][y1]+=c
a[x1][y2+1]−=ca[x_1][y_2+1] -= ca[x1][y2+1]−=c
a[x2+1][y1]−=ca[x_2+1][y_1] -= ca[x2+1][y1]−=c
a[x2+1][y2+1]+=ca[x_2+1][y_2+1] += ca[x2+1][y2+1]+=c
更多内容欢迎关注我的公众号ACJavaBear,一起学Java吧~