前缀和学习笔记与总结

前缀和学习笔记与总结

前缀和

一维前缀和

What

现有 原数组

a1,a2,a3,,an

对应的 前缀和数组 应满足:

Si=a1+a2+a3++an

前缀和 Si 即为 原数组 中前 i 个数的和

如何求

s[0] = 0;
for (int i = 1; i <= n; i ++ )
	s[i] = s[i - 1] + a[i];

i = 1 开始 和 定义 s[0] = 0,是为了解决边界问题。

对于求区间 [l,r] 的和,可通过 s[r] - s[l - 1]
证明过程如下:

Sr=a1+a2++al1+al++arSl1=a1+a2++al1SrSl1=al++ar=i=lrai

前缀和处理为 O(N),查询为 O(1)

作用

快速地求出原数组中 连续 一段数的
(由 O(n) 优化到 O(1)

公式

Sr=a1+a2++al1+al++arSrSl1=al++ar

模板

S[i] = a[1] + a[2] + ... a[i];
a[l] + ... + a[r] = S[r] - S[l - 1];

模板题目

AcWing 795. 前缀和 题目入口

题目大意

输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

CODE

点击查看代码
#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], 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;
}

二维前缀和

What

现有一个矩阵

如图

阴影部分表示 S4,3,即 左上端点为 (1,1),右下端点为 (4,3) 的矩阵 中数的总和

所以 Si,j 就是下图中的阴影部分的总和

Si,j 怎么算

同样,我们也采用递推的方式,用前面已经求出的 S 来求 Si,j

对于 原数组 A,前缀和数组 S,如下图

Si1,jSi,j1 相加将多出一个 Si1,j1(即紫色重叠部分),所以需要减去一个 Si1,j1.
在加上 (i,j) 本身,即加上 Ai,j.

所以

Si,j=Si1,j+Si,j1Si1,j1+Ai,j

for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[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];

矩阵的和

对于 左上端点为 (x1,y1),右下端点为 (x2,y2) 的矩阵

如图

Sx2,y2 减去 Sx2,y11Sx11,y2Sx11,y11(紫色部分)被多减了一次。

所以

Sum=Sx2,y2Sx2,y11Sx11,y2+Sx11,y11

Sum = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]

公式

Si,j=Si1,j+Si,j1Si1,j1+Ai,j

Sum=Sx2,y2Sx2,y11Sx11,y2+Sx11,y11

模板

S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[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];

Sum = S_{x_2,y_2}-S_{x_2,y_1-1}-S_{x_1-1,y_2}+S_{x_1-1,y_1-1}

模板题目

AcWing 796. 子矩阵的和 题目入口

题目大意

输入一个 nm 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。

CODE

点击查看代码
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
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", &s[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];

    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 @   Mingrui_Yang  阅读(12)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示