前缀和与差分Java代码模板

本文详细介绍了前缀和的概念、作用,包括一维和二维前缀和的应用,并提供了二维前缀和计算矩形区域和的方法。接着讲解了差分的定义,说明了差分在区间加法操作中的高效性,以及一维和二维差分的更新规则。通过实例加深了读者对前缀和和差分的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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=bn1+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}an4呢?如果有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[l1] 即可得到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[l1]+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[l1]=a[1]+a[2]+...+a[l1]

所以 b[r]−b[l−1]=a[l]+...+a[r]b[r] - b[l-1] = a[l] + ... + a[r]b[r]b[l1]=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}bnmanma_{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][y11]b[x11][y2]+b[x11][y11]

证明就自己画个图就好了,还是比较简单的。记忆起来也比较容易,下标为1的都减一。

差分

1.什么是差分

可以理解为前缀和的逆运算。怎么理解?直接看个例子吧。

设有一差分数组{an}\{a_n\}{an} ,其前缀和数组为{bn}\{b_n\}{bn}

则差分数组

a1=b1a_1 = b_1a1=b1

a2=b2−b1a_2 = b_2 - b_1a2=b2b1

a3=b3−b2a_3 = b_3 - b_2a3=b3b2

.........

an=bn−bn−1a_n = b_n - b_{n-1}an=bnbn1

而前缀和数组

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}an5 这一段上每一个元素加上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+a2c=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+a2c+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+cb43b14b24b34b44c

最后 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+ca32a42ca13a23a33a43a14a24ca34a44+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吧~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值