前缀和

发布时间 2023-04-22 22:40:12作者: NachoNeko

算法简介

前缀和用于快速得到数组某个连续区间内所有元素的元素和。

时间复杂度

构建前缀和数组:\(O(n)\)
求取某区间总和:\(O(1)\)

实现原理

按照如下规则构建前缀和数组:

例如:有数组 \(a\),前缀和数组为 \(s\)
\(s[0] = 0\)
\(s[1] = a[1]\)
\(s[2] = a[2] + a[1]\)
...
\(s[n] = a[n] + a[n-1] + a[n-2] + \cdots + a[1]\)

那么求取某一区间 [l,r] 即可用 s[r] - s[l-1] 取得

如求 \(a[3] + a[4] + a[5]\) 的和,则有:
\(s[5] = a[5] + a[4] + a[3] + a[2] + a[1]\)
\(s[2] = a[2] + a[1]\)
则有 \(a[3] + a[4] + a[5] = s[5] - s[2] = a[5] + a[4] + a[3] + a[2] + a[1] - a[2] - a[1]\)

算法实例

1. 题目描述

https://www.acwing.com/problem/content/description/797/

2. AC代码

#include <bits/stdc++.h>

const int N = 100010;

int n,m;
int sum[N],a[N];

int main() {
    std::cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) {
        std::cin >> a[i];
    }
    
    for (int i = 1; i <= n; i ++ ) {
        sum[i] = sum[i - 1] + a[i];
    }
    
    while(m -- ) {
        int l,r;
        std::cin >> l >> r;
        std::cout << sum[r] - sum[l - 1] << "\n";
    }
    
    return 0;
}

算法拓展

二维前缀和

二维前缀和就是将一维前缀和扩展到二维,快速求取某个子矩阵元素和的算法。

其中前缀和数组 sum[i,j] 表示 [1,1] 到 [i,j] 这个子矩阵的元素和。

sum[i,j] 即为红色框矩阵的和:

求解二维前缀和需要明确两个问题:

  1. 如何构造 sum 数组:
    \(sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[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];

  1. 如何求解以 [x1,y1] 为左上角坐标,[x2,y2] 为右下角坐标的子矩阵的矩阵和。
    \(sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]\)


绿色矩形的面积 = 整个外围面积s[x2, y2] - 黄色面积s[x2, y1 - 1] - 紫色面积s[x1 - 1, y2] + 重复减去的红色面积 s[x1 - 1, y1 - 1]

题目实例

https://www.acwing.com/problem/content/description/798/

AC代码

#include <iostream>

using namespace std;

typedef long long LL;

const int N = 1010;

LL a[N][N],s[N][N];

int main()
{
    int n,m,q;
    cin >> n >> m >> q;
    
    for (int i = 1 ; i <= n ; i ++ )
        for (int j = 1 ; j <= m ; j ++ )
            cin >> a[i][j];
            
    for (int i = 1 ; i <= n ; i ++ )
        for (int j = 1 ; j <= m ; j ++ )
            s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j];
    
    while(q--)
    {
        int x1,y1,x2,y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] << endl;
    }
    return 0;
}

参考

[1] https://www.acwing.com/solution/content/3797/
[2] https://www.acwing.com/solution/content/27301/