前缀和
现在有一道题:
输入一个长度为 n n n 的整数序列。
接下来再输入 m m m 个询问,每个询问输入一对 l , r l, r l,r。
对于每个询问,输出原序列中从第 l l l 个数到第 r r r 个数的和。
数据范围
1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n
1 ≤ n , m ≤ 100000 1 \le n,m \le 100000 1≤n,m≤100000
− 1000 ≤ 数列中元素的值 ≤ 1000 -1000 \le 数列中元素的值 \le 1000 −1000≤数列中元素的值≤1000
这种题大家一看就知道打暴力,确实是个好方法 :

那么我们该如何优化它呢?
我们维护一个名为
s
s
s 的前缀和数组,且
s
[
i
]
=
∑
j
=
1
i
a
[
j
]
s[i] = \sum\limits _ {j = 1}^{i} a[j]
s[i]=j=1∑ia[j],如下:
for (int i = 1; i <= n; ++ i ) s[i] = s[i - 1] + a[i];
如果我们维护的序列是
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
1,2,3,4,5,6,7,8,9,10
1,2,3,4,5,6,7,8,9,10 的话,那么我们的前缀和数组为
1
,
3
,
6
,
10
,
15
,
21
,
28
,
36
,
45
,
55
1,3,6,10,15,21,28,36,45,55
1,3,6,10,15,21,28,36,45,55 ,那我们该如何利用前缀和数组的性质求和呢?
可以看下面的图:

假如说我们要求
[
l
,
r
]
[l, r]
[l,r] 数值的和,我们只需要将
s
[
r
]
s[r]
s[r] (红色部分)减去
s
[
l
−
1
]
s[l - 1]
s[l−1] (蓝色部分)即可。代码:
printf("%d\n", s[r] - s[l - 1]);
完整代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010;
int n, m;
int a[N];
int s[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i ) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++ i ) s[i] = s[i - 1] + a[i];
while ( m -- ) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
二维前缀和
题目:
输入一个 n n n 行 m m m 列的整数矩阵,再输入 q q q 个询问,每个询问包含四个整数 x 1 , y 1 , x 2 , y 2 x_1, y_1, x_2, y_2 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
数据范围
1 ≤ n , m ≤ 1000 1 \le n,m \le 1000 1≤n,m≤1000
1 ≤ q ≤ 200000 1 \le q \le 200000 1≤q≤200000
1 ≤ x 1 ≤ x 2 ≤ n 1 \le x_1 \le x_2 \le n 1≤x1≤x2≤n
1 ≤ y 1 ≤ y 2 ≤ m 1 \le y_1 \le y_2 \le m 1≤y1≤y2≤m
− 1000 ≤ 矩阵内元素的值 ≤ 1000 -1000 \le 矩阵内元素的值 \le 1000 −1000≤矩阵内元素的值≤1000
二维前缀和,可以用来求一个矩阵里人任意一个子矩阵内数的和。
我们用
s
[
I
]
[
J
]
s[I][J]
s[I][J] 表示
∑
i
=
1
I
∑
j
=
1
J
a
[
i
]
[
j
]
\sum\limits_{i = 1}^{I} \sum\limits_{j = 1}^{J} a[i][j]
i=1∑Ij=1∑Ja[i][j]。
所以
s
[
I
]
[
J
]
(绿色部分)
=
s
[
I
−
1
]
[
J
]
(蓝色部分)
+
s
[
I
]
[
J
−
1
]
(红色部分)
−
s
[
I
−
1
]
[
J
−
1
]
(紫色部分)
+
a
[
I
]
[
J
]
s[I][J](绿色部分) = s[I - 1][J](蓝色部分) + s[I][J - 1](红色部分) - s[I - 1][J - 1] (紫色部分)+ a[I][J]
s[I][J](绿色部分)=s[I−1][J](蓝色部分)+s[I][J−1](红色部分)−s[I−1][J−1](紫色部分)+a[I][J]

查询的话,我们将上面所有颜色的框改一下位置就好了;假如我们要求
(
x
1
,
y
1
)
(x_1, y_1)
(x1,y1) 到
(
x
2
,
y
2
)
(x_2, y_2)
(x2,y2) 的和,即:
s
[
x
2
]
[
y
2
]
−
s
[
x
1
−
1
]
[
y
2
]
−
s
[
x
2
]
[
y
1
−
1
]
+
s
[
x
1
−
1
]
[
y
1
−
1
]
s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
s[x2][y2]−s[x1−1][y2]−s[x2][y1−1]+s[x1−1][y1−1]
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N];
int s[N][N];
int main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= m; ++ j )
scanf("%d", &a[i][j]);
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= m; ++ j )
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
while ( q -- ) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &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;
}
小学六年级,最后一个六一辣
4247b82b-6f44-4c83-82ae-f84b5e8c0e91
1466

被折叠的 条评论
为什么被折叠?



