一维前缀和
数组元素(这里规定下标从1开始,a[0] = 0,下文解释): a 1 + a 2 + a 3 . . . + a n a_1 + a_2 + a_3 ...+ a_n a1+a2+a3...+an
前缀和公式: S i S_i Si = a 1 + a 2 + a 3 . . . + a i a_1 + a_2 + a_3 ...+ a_i a1+a2+a3...+ai
如何求 S i S_i Si
s[0] = 0;//下标从1开始,a[0] = 0;
for(int i = 1; i <= n; i++){
s[i] = s[i - 1] + a[i];
}
前缀和的作用
例如:计算 a l + a l + 1 + a l + 2 . . . + a r a_l+ a_ {l + 1}+ a_{l + 2} ...+ a_{r} al+al+1+al+2...+ar,就是求集合中[l, r]元素的和
使用前缀和的解决方式:s[r] - s[l -1] =( a 1 + a 2 + a 3 . . . + a r a_1 + a_2 + a_3 ...+ a_r a1+a2+a3...+ar)- ( a 1 + a 2 + a 3 . . . + a l a_1 + a_2 + a_3 ...+ a_l a1+a2+a3...+al)= a l + a l + 1 + a l + 2 . . . + a r a_l+ a_ {l + 1}+ a_{l + 2} ...+ a_{r} al+al+1+al+2...+ar
这样做的优点在于提高了速度,如果从l遍历到n进行计算,时间复杂度为O(n),而使用前缀和的方式,时间复杂度为O(1)
那么解释一下为什么元素下标从1开始呢,是为了处理边界 。由a[0] = 0,可以得出s[0] = 0。如果想要计算[1, 10],则计算s[10] - s[0],这里s[0] = 0,所以在边界情况就不需要特判了。
题目链接:前缀和
#include<iostream>
using namespace std;
const int N = 100005;
int a[N];
int s[N];
int main(){
int n, m, l, r;
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> a[i];//读入数组元素
s[i] =a[i] + s[i - 1];//更新前缀和数组
}
for(int i = 1; i <= m; i++){
cin >> l >> r;
cout << s[r] - s[l - 1] <<endl;//求【l,r】元素的和
}
return 0;
}
二维前缀和
二维前缀和是用来在一个矩阵中求子矩阵的和。例如我们用s[3][3]
表示下面这个绿色区域的元素总和。
(x1, y1) (x2, y2)这一子矩阵中所有数的和如何计算
s[i][j]
如何计算
用二维数组a存储矩阵中每一个元素,可以用以下方式计算s[i][j]
题目链接:子矩阵的和
#include<iostream>
using namespace std;
int a[1005][1005];
int s[1005][1005];
int main(){
int n, m, q;
int x1, y1, x2, y2;
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
while(q --){
cin >> x1 >> y1 >> x2 >> y2;
printf("%d\n",s[x2][y2] - s[x1 -1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
}
return 0;
}