前缀和(1~3维)

前缀和

当遇到一个这样的情景,需要你求一个数组aaaala_lal~ara_rar,但是又不止一次询问,所以需要用O(N)O(N)O(N)的时间复杂度求出,就可以用到前缀和。

一维前缀和

顾名思义,对于数组aaa求出的一维前缀和数组sss中的第iii位表示的就是从数组aaa的第一位一直到第iii位的元素和,此时有:
si=si−1+ai s_i=s_{i-1}+a_i si=si1+ai
同时也有:
s区间和=sr−sl−1 s_{区间和}=s_r-s_{l-1} s区间和=srsl1
这些蒟蒻都会。

二维前缀和

这是对于一个二维的矩形块求其中一块的面积。
色盲
可以看上面这个表格,是的就是他没错了。
当我们知晓大长方形的面积,需要求小长方形\color{red}小长方形小长方形,可以看出:
S=S总−S−S−S \color{red}S=\color{black}S_总-\color{orange}S-\color{blue}S-\color{green}S S=SSSS
但是我们不知道S\color{orange}SSS\color{blue}SS,所以可以考虑变形:
S=S总−(S+S)−(S+S)+S \color{red}S=\color{black}S_总-(\color{orange}S+\color{green}S\color{black})-(\color{blue}S+\color{green}S\color{black})+\color{green}S S=S(S+S)(S+S)+S
虽然对色盲有些不友好
S+S\color{orange}S+\color{green}SS+SS+S\color{blue}S+\color{green}SS+S还有S\color{green}SS我们都知道,所以S\color{red}SS也可以求出了。同理,也可以反推出S总S_总S
最终有:
si,j=si−1,j+si,j−1−si−1,j−1+ai,j s_{i,j}=s_{i-1,j}+s_{i,j-1}-s_{i-1,j-1}+a_{i,j} si,j=si1,j+si,j1si1,j1+ai,j
推广有(区域和为顶点坐标分别为lllrrrqqqppp的矩形):
s区域和=sl,r−sq−1,r−sl,p−1+sq−1,p−1 s_{区域和}=s_{l,r}-s_{q-1,r}-s_{l,p-1}+s_{q-1,p-1} s区域和=sl,rsq1,rsl,p1+sq1,p1
这个大多数人也会。
那么……

三维前缀和

对于一个立方体,从中间扣一块出来问你它的体积。
在已知前缀和的情况下,可以简化问题为:

  • 求一个立方体一个角块的体积。

在这里插入图片描述

一个正方体,如上图,六个面分别为s1…6s_{1\ldots6}s16
六面的平面图如下:
在这里插入图片描述
先删除绿色的部分。
在这里插入图片描述
在这里插入图片描述
接着删掉蓝色的部分,此时粉色的部分表示重叠部分。
在这里插入图片描述
在这里插入图片描述
再次删除黄色的部分,粉色部分表示重叠一次,紫色部分表示重叠两次。
78
在这里插入图片描述
所以,不难得知结论。
V\color{green}VVV\color{blue}VV的重合部分为V1\color{pink}V_1V1V\color{green}VVV\color{orange}VV的重合部分为V2\color{pink}V_2V2V\color{orange}VVV\color{blue}VV的重合部分为V3\color{pink}V_3V3),三者重合为V\color{purple}VV,以立体图中为准,有:
V=V总−(V+V+V−V1−V2−V3)−V \color{red}V\color{black}=V_总-(\color{green}V\color{black}+\color{blue}V\color{black}+\color{orange}V\color{black}-\color{pink}V_1\color{black}-\color{pink}V_2\color{black}-\color{pink}V_3\color{black})-\color{purple}V V=V(V+V+VV1V2V3)V
化简为:
V=V总−V−V−V+V1+V2+V3−V \color{red}V\color{black}=V_总-\color{green}V\color{black}-\color{blue}V\color{black}-\color{orange}V\color{black}+\color{pink}V_1\color{black}+\color{pink}V_2\color{black}+\color{pink}V_3\color{black}-\color{purple}V V=VVVV+V1+V2+V3V
最终得出:
Vi,j,k=Vi−1,j−1,k−1+Vi−1,j,k+Vi,j−1,k+Vi,k,k−1−Vi−1,j−1,k−Vi−1,j,k−1−Vi,j−1,k−1+ai,j,k V_{i,j,k}=V_{i-1,j-1,k-1}+V_{i-1,j,k}+V_{i,j-1,k}+V_{i,k,k-1}-V_{i-1,j-1,k}-V_{i-1,j,k-1}-V_{i,j-1,k-1}+a_{i,j,k} Vi,j,k=Vi1,j1,k1+Vi1,j,k+Vi,j1,k+Vi,k,k1Vi1,j1,kVi1,j,k1Vi,j1,k1+ai,j,k
推广为(一个对角顶点坐标分别是iiijjjkkkuuuvvvwww):
V区块体积=Vi,j,k−Vu−1,j,k−Vi,v−1,k−Vi,j,w−1+Vu−1,v−1,k+Vu−1,j,w−1+Vi,v−1,w−1−Vu−1,v−1,w−1 V_{区块体积}=V_{i,j,k}-V_{u-1,j,k}-V_{i,v-1,k}-V_{i,j,w-1}+V_{u-1,v-1,k}+V_{u-1,j,w-1}+V_{i,v-1,w-1}-V_{u-1,v-1,w-1} V区块体积=Vi,j,kVu1,j,kVi,v1,kVi,j,w1+Vu1,v1,k+Vu1,j,w1+Vi,v1,w1Vu1,v1,w1

### 三前缀和算法解释 三前缀和是一种用于快速计算多数组子区域和的技术。其核心思想是通过预处理构建一个辅助数据结构——前缀和矩阵,从而使得后续查询操作的时间复杂度降低到常数级别。 对于给定的一个三数组 `arr[x][y][z]`,定义它的三前缀和为: \[ prefixSum[i][j][k] = \sum_{x=0}^{i}\sum_{y=0}^{j}\sum_{z=0}^{k} arr[x][y][z] \] 这意味着 `prefixSum[i][j][k]` 表示从原点 `(0, 0, 0)` 到位置 `(i, j, k)` 的所有元素之和[^1]。 #### 构建三前缀和 为了高效地计算三前缀和,可以利用动态规划的思想逐步扩展度。以下是具体的实现方法: ```python def build_3d_prefix_sum(arr): n, m, l = len(arr), len(arr[0]), len(arr[0][0]) prefixSum = [[[0 for _ in range(l)] for _ in range(m)] for _ in range(n)] for i in range(n): for j in range(m): for k in range(l): current_value = arr[i][j][k] # Add previous values based on inclusion-exclusion principle prefixSum[i][j][k] += current_value if i > 0: prefixSum[i][j][k] += prefixSum[i-1][j][k] if j > 0: prefixSum[i][j][k] += prefixSum[i][j-1][k] if k > 0: prefixSum[i][j][k] += prefixSum[i][j][k-1] # Subtract overlapping regions to avoid double-counting if i > 0 and j > 0: prefixSum[i][j][k] -= prefixSum[i-1][j-1][k] if i > 0 and k > 0: prefixSum[i][j][k] -= prefixSum[i-1][j][k-1] if j > 0 and k > 0: prefixSum[i][j][k] -= prefixSum[i][j-1][k-1] # Add back the triple-overlapping region if i > 0 and j > 0 and k > 0: prefixSum[i][j][k] += prefixSum[i-1][j-1][k-1] return prefixSum ``` 上述代码实现了三前缀和的构建过程。它基于包含排除原则来消除重复计数的影响[^2]。 #### 查询任意子立方体的和 一旦构建好了三前缀和表,就可以轻松地查询任何子立方体范围内的元素总和。假设要查询从 `(x1, y1, z1)` 到 `(x2, y2, z2)` 范围内的元素和,则可以通过如下方式完成: ```python def query_subcube(prefixSum, x1, y1, z1, x2, y2, z2): result = prefixSum[x2][y2][z2] if x1 > 0: result -= prefixSum[x1-1][y2][z2] if y1 > 0: result -= prefixSum[x2][y1-1][z2] if z1 > 0: result -= prefixSum[x2][y2][z1-1] if x1 > 0 and y1 > 0: result += prefixSum[x1-1][y1-1][z2] if x1 > 0 and z1 > 0: result += prefixSum[x1-1][y2][z1-1] if y1 > 0 and z1 > 0: result += prefixSum[x2][y1-1][z1-1] if x1 > 0 and y1 > 0 and z1 > 0: result -= prefixSum[x1-1][y1-1][z1-1] return result ``` 此函数利用了已有的前缀和表,在 O(1) 时间内返回指定范围内元素的总和[^3]。 ### 总结 三前缀和的核心在于预先计算并存储部分结果以便于后续快速访问。这种方法特别适合需要频繁执行区间求和运算的应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值