Loading

前缀和

前缀和

现在有一道题:

输入一个长度为 \(n\) 的整数序列。

接下来再输入 \(m\) 个询问,每个询问输入一对 \(l, r\)

对于每个询问,输出原序列中从第 \(l\) 个数到第 \(r\) 个数的和。

数据范围

\(1 \le l \le r \le n\)
\(1 \le n,m \le 100000\)
\(-1000 \le 数列中元素的值 \le 1000\)

这种题大家一看就知道打暴力,确实是个好方法

在这里插入图片描述

那么我们该如何优化它呢?
我们维护一个名为 \(s\) 的前缀和数组,且 \(s[i] = \sum\limits _ {j = 1}^{i} a[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,3,6,10,15,21,28,36,45,55\) ,那我们该如何利用前缀和数组的性质求和呢?
可以看下面的图:
在这里插入图片描述
假如说我们要求 \([l, r]\) 数值的和,我们只需要将 \(s[r]\) (红色部分)减去 \(s[l - 1]\) (蓝色部分)即可。代码:

printf("%d\n", s[r] - s[l - 1]);

完整代码:

#include <cstring>
#include <iostream>
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\)\(m\) 列的整数矩阵,再输入 \(q\) 个询问,每个询问包含四个整数 \(x_1, y_1, x_2, y_2\),表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

数据范围

\(1 \le n,m \le 1000\)
\(1 \le q \le 200000\)
\(1 \le x_1 \le x_2 \le n\)
\(1 \le y_1 \le y_2 \le m\)
\(-1000 \le 矩阵内元素的值 \le 1000\)

二维前缀和,可以用来求一个矩阵里人任意一个子矩阵内数的和。
我们用 \(s[I][J]\) 表示 \(\sum\limits_{i = 1}^{I} \sum\limits_{j = 1}^{J} 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_2, y_2)\) 的和,即:\(s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]\)
代码:

#include <cstring>
#include <iostream>
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;
}
posted @ 2023-06-01 21:46  popcoount  阅读(10)  评论(0编辑  收藏  举报  来源