关于一维数组的前缀和【差分与前缀和,数组的常见预处理技巧】
一维数组的前缀和实现了常数时间内求取一个区间的加和,同样地,二维数组的前缀和也是在常数时间内求取某个区间的所有元素加和
如果有一维数组a(下标从1开始),他的前缀和数组为sum(sum[0] = 0)
我们轻易的知道:区间[l, r]
的元素加和 = sum[r]-sum[l-1]
同样的,一个矩阵的子矩阵,假设有 r 行 c 列,那么其实就是在原来的 r 个一维数组的前缀和数组的某个区间的加和:
观察可以发现,这些结果,是某两列的子区间元素的加和,换句话说,还是一维数组(某列)的子区间的元素加和
我们通过对 每一行的前缀和数组组成的矩阵的每一列做列方向的前缀和 ,可以有效的节省上述计算,用常数时间完成一个子矩阵的加和查询
注意
行方向的前缀和和列方向的,一定得分开做,因为列方向的前缀和是基于行前缀和数组组成的矩阵的每一列的
原矩阵最好是从1,1下标开始,方便做前缀和的边界情况
查询
那么对列方向做前缀和,之后得到的矩阵前缀和如何查询呢?假设得到的矩阵前缀和(也是一个矩阵)为a
原矩阵的 x1, y1
下标为左上角,x2, y2
下标为右下角,组成的子矩阵的加和,为
(a[x2][y2]-a[x1-1][y2]) - (a[x2][y1-1]-a[x1-1][y1-1]);
打开括号
a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
图解查询
就有点类似求集合的样子
a[i][j]
的值,表示矩阵左上角到 i, j
下标的所有元素加和
代码
#include <bits/stdc++.h>
using namespace std;
int a[1509][1509];
int m, n, x1, y1, x2, y2;
int kksum(int x1, int y1, int x2, int y2)
{
return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}
int main()
{
cin>>m>>n;
for(int i=0; i<=m; i++) a[i][0]=0;
for(int i=0; i<=n; i++) a[0][i]=0;
// 计算每一行前缀和
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
cin>>a[i][j], a[i][j]+=a[i][j-1];
// 计算每一列前缀和,一定要和行分开算
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
a[i][j] += a[i-1][j];
// 输出前缀和矩阵
cout<<endl;
for(int i=1; i<=m; i++)
{
for(int j=1; j<=n; j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
// 验证求和
cout<<endl;
while(1)
{
cin>>x1>>y1>>x2>>y2;
cout<<"sum of range is "<<kksum(x1, y1, x2, y2)<<endl;
}
return 0;
}
/*
2 3
1 2 3
4 5 6
*/