前缀和与差分

前缀和

定义

对于一个给定的数列 AA,可以计算得到它对应的前缀和数列 SS,其中 SS 的第 ii 项是数列 AA 的前 ii 项之和。

S[i]=j=1iA[j]S[i] = \sum_{j = 1}^i A[j]

理解

前缀和可以简单理解为「数列的前 nn 项的和」,是一种重要的预处理方式。
根据定义,前缀和数列可以通过递推的方式计算得到:

S[i]=j=1iA[j]={A[i], i=1S[i1]+A[i], i>1S[i] = \sum_{j = 1}^i A[j] = \left \{ \begin{array}{ll} A[i], \ i = 1 \\ S[i-1] + A[i], \ i > 1 \end{array} \right.

因此,可以在线性时间内得到前缀和数列。

具体应用

基于前缀和数列 SS,可以通过一次减法代替累加操作,得到原数列 AA 的区间和(下标 llrr 的元素和),能够大大降低查询的时间复杂度。

sum(l,r)=i=lrA[i]=S[r]S[l1]sum(l,r) = \sum_{i = l}^r A[i] = S[r] - S[l-1]

拓展应用

二维前缀和

对于一个给定的二维数组 AA,可以计算得到它对应的二维前缀和 SS,其中 S[i][j]S[i][j]AA 的前 ii 行前 jj 列之和。

S[i][j]=x=1iy=1jA[x][y]S[i][j] = \sum_{x = 1}^i \sum_{y = 1}^j A[x][y]

与一维前缀和类似,二维前缀和涉及到两个关键问题:如何计算 和 如何应用。

1. 如何计算?

二维前缀和可以基于容斥原理进行递推得到:

S[i][j]=x=1iy=1jA[x][y]={A[i][j], i=1&j=1S[i][j1]+A[i][j], i=1&j>1S[i1][j]+A[i][j], i>1&j=1S[i1][j]+S[i][j1]S[i1][j1]+A[i][j], i>1&j>1S[i][j] = \sum_{x = 1}^i \sum_{y = 1}^j A[x][y] = \left \{ \begin{array}{llll} A[i][j], \ i = 1 \And j = 1 \\ S[i][j-1] + A[i][j], \ i = 1 \And j > 1 \\ S[i-1][j] + A[i][j], \ i > 1 \And j = 1 \\ S[i-1][j] + S[i][j-1] - S[i-1][j-1] + A[i][j], \ i > 1 \And j > 1 \end{array} \right.

2. 如何应用?

基于二维前缀和 SS 和容斥原理,可以通过三次加(减)法代替累加操作,得到原二维数组 AA 的区域和(以(x1,y1)(x_1, y_1)为左上顶点,以(x2,y2)(x_2, y_2)为右下顶点的子矩阵中的元素和),能够大大降低查询的时间复杂度。

sum(x1,y1,x2,y2)=x=x1x2y=y1y2A[x][y]=S[x2][y2]S[x11][y2]S[x2][y11]+S[x11][y11]sum(x_1, y_1, x_2, y_2) = \sum_{x = x_1}^{x_2} \sum_{y = y_1}^{y_2} A[x][y] = S[x_2][y_2] - S[x_1-1][y_2] - S[x_2][y_1-1] + S[x_1-1][y_1-1]

差分

定义

对于一个给定的数列 AA,可以计算得到它对应的差分数列 DD,其中 DD 的第 11 项是数列 AA 的第 11 项,DD 的第 ii 项(i>1i > 1)是数列 AA 的第 ii 项与第 i1i - 1 项之差。

D[i]={A[i], i=1A[i]A[i1], i>1D[i] = \left \{ \begin{array}{ll} A[i], \ i = 1 \\ A[i] - A[i-1], \ i > 1 \end{array} \right.

理解

差分也是一种重要的预处理方式,与前缀和是一对互逆运算,两者之间存在如下的转换关系:

  1. 差分数列的前缀和数列是原数列,即

    A[i]=j=1iD[j]A[i] = \sum_{j = 1}^i D[j]

  2. 前缀和数列的差分数列是原数列,即

    A[i]={S[i], i=1S[i]S[i1], i>1A[i] = \left \{ \begin{array}{ll} S[i], \ i = 1 \\ S[i] - S[i-1], \ i > 1 \end{array} \right.

具体应用

基于差分数列 DD,可以将原数列上的“区间加操作”转化为差分数列上的“单点操作”,能够大大降低操作的时间复杂度。

将原数列 AA 中下标处于 [l,r][l,r] 范围内的元素都加上 numnum,这一“区间加操作”在差分数列 DD 上对应的变化是“单点操作”,即 DlD_lnumnumDr+1D_{r+1}numnum,其他位置不变。因此,可以通过差分数列上的“单点操作”代替原数列上的“区间加操作”。如果要获得经过多次“区间加操作”后的原数列,只需要对经过多次“单点操作”后的差分数列计算一次前缀和数列即可。