欢迎光临
我们一直在努力

前缀和(Prefix Sum)

基本概念

  • 前缀和的定义:在C++算法与数据结构领域,前缀和是一种优化时间复杂度的有效手段,尤其适用于频繁查询数组区间和的场景。它通过预先计算“前缀累积和”,将原本使用for循环枚举O(n)的时间复杂度压缩至O(1),是一种高频使用的优化技巧。
  • 数学表示:

    1. 基础数学符号定义

    假设存在原数组:A=[a1​,a2​,a3​,…,an​](下标从 1 开始,便于理解)

  • 定义前缀和数组:S=[s0​,s1​,s2​,…,sn​](通常令 s0​=0,作为边界条件,简化计算)

  • 2. 前缀和的递推公式(核心)

    前缀和数组的每个元素满足:

    si​={0si−1​+ai​​i=0i≥1​

    展开后也可以表示为:

    si​=∑k=1i​ak​=a1​+a2​+…+ai​

  • 核心思想:空间换时间,预处理后实现O(1)区间查询

一维前缀和

  • 构建方法:线性遍历原数组,递推计算 #include<iostream>
    using namespace std;

    int a[110], preSum[110];

    //下标 1 2 3 4 5
    //a[] 2 4 6 8 10
    //ps[] 2 6 12 20 30
    int main() {
    //n个元素,m次查询,每次查询[l,r]的元素之和
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {
    cin >> a[i];
    //前i项和=前i-1项和+第i项
    preSum[i] = preSum[i – 1] + a[i];//O(n)
    }

    int m; cin >> m;
    while (m–) {
    int l, r; cin >> l >> r;
    int sum = 0;
    //for (int i = l; i <= r; i++) sum += a[i];//O(n)
    sum = preSum[r] – preSum[l – 1];//o(1)
    cout << sum << endl;
    }

    return 0;
    }

  • 查询区间和:sum[l..r] = S[r+1] – S[l]
  • 应用场景:固定数组频繁区间求和(如子数组和统计)

二维前缀和

推导步骤:从 “拆分面积” 到核心公式

步骤 1:直观拆解矩形面积(初步思路)

要计算 S[i][j]((1,1) 到 (i,j) 的矩形和),我们可以先尝试用一维前缀和的思路拆分:

  • 先看 “前 i−1 行所有元素的和”:即 S[i−1][j]((1,1) 到 (i−1,j) 的矩形和);
  • 再加上 “第 i 行前 j 个元素的和”:即一维前缀和 ∑k=1j​A[i][k]。

此时初步表达式:S[i][j]=S[i−1][j]+∑k=1j​A[i][k]

但这个表达式还不够 “二维”—— 我们需要把 “第 i 行前 j 个元素的和” 也用二维前缀和表示,避免重复计算。

步骤 2:发现重复区域,引入补偿思想

换一种拆分方式:把 (1,1) 到 (i,j) 的矩形拆分为几部分(如下图逻辑):

步骤 3:推导核心递推公式

根据上述拆分逻辑,用前缀和符号替换 “区域”:

  • 区域 1 = S[i−1][j](上侧矩形和);
  • 区域 2 = S[i][j−1](左侧矩形和);
  • 区域 3 = S[i−1][j−1](重叠区域和);
  • 区域 4 = A[i][j](当前元素)。

因此得到二维前缀和的核心递推公式:

S[i][j]=S[i−1][j]+S[i][j−1]−S[i−1][j−1]+A[i][j]

综上可得实现代码如下:

#include<iostream>
using namespace std;

int a[110][110], preSum[110][110];

int main() {
//n个元素,m次查询,每次查询lx,ly和rx,ry围成的矩阵和
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
//1,1点到i,j点围成矩阵和-二维前缀和
preSum[i][j] = preSum[i – 1][j] + preSum[i][j – 1] – preSum[i – 1][j – 1] + a[i][j];
}
}

int k; cin >> k;
while (k–) {
//求lx,ly和rx,ry围成的矩阵和
int lx, ly, rx, ry; cin >> lx >> ly >> rx >> ry;
int sum = 0;
//for()
// for()
//二维前缀和求矩阵和
sum = preSum[rx][ry] – preSum[lx – 1][ry] – preSum[rx][ly – 1] + preSum[lx – 1][ly – 1];//O(1)
cout << sum << endl;
}

return 0;
}

差分与前缀和的互逆性

  • 差分数组定义:d[i] = a[i] – a[i-1](i ≥ 1)
  • 前缀和恢复原数组:对差分数组求前缀和
  • 应用:区间增减操作(如批量对[l,r]加k)转化为差分数组单点修改

常见应用场景

  • 快速统计固定数组的区间和/子矩阵和
  • 结合哈希表优化特定问题(如寻找和为k的子数组)
  • 动态维护离线查询(如多次区间和请求)

复杂度分析

  • 预处理时间:O(n)(一维)、O(mn)(二维)
  • 查询时间:O(1)
  • 空间复杂度:O(n)(一维)、O(mn)(二维)

实战例题

洛谷 1115. 最大子段和(一维前缀和)

题解如下:

#include<iostream>
using namespace std;

const int N = 2e5 + 10;
int a[N], preSum[N];

int main() {

int n; cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
preSum[i] = preSum[i – 1] + a[i];
}
int maxx = -0x3f3f3f3f;
//枚举所有的子段(连续一段区间)
for (int l = 1; l <= n; l++) {
for (int r = l; r <= n; r++) {
//求解子段[l,r]的和
int sum = preSum[r] – preSum[l – 1];
if (sum > maxx) maxx = sum;
}
}
cout << maxx << endl;
return 0;
}

 信息学奥赛一本通(1224:最大子矩阵)(二维前缀和)

以下为题解:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e3 + 10;
int a[N][N], s[N][N];
//s[i][j]代表1,1点到i,j点围成的矩形的元素和(二维前缀和)
int main() {

int n; cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
//二维前缀和的递推式
s[i][j] = s[i – 1][j] + s[i][j – 1] – s[i – 1][j – 1] + a[i][j];
}
}

int mx = -0x3f3f3f3f;
//先枚举左上角点
for (int lx = 1; lx <= n; lx++) {
for (int ly = 1; ly <= n; ly++) {

//枚举右下角点
for (int rx = lx; rx <= n; rx++) {
for (int ry = ly; ry <= n; ry++) {

//求左上角点lx,ly和右下角点rx,ry围成的矩形的元素之和(矩阵和)
int sum = s[rx][ry] – (s[lx – 1][ry] + s[rx][ly – 1] – s[lx – 1][ly – 1]);
mx = max(mx, sum);
}
}
}
}
cout << mx << endl;
return 0;
}

    注意事项

    • 明确下标规则(0/1-based),保持递推、查询公式一致,避免l-1等操作越界,推荐 1-based 简化边界处理;
    • 防范数据溢出,二维前缀和风险更高,大数值 / 高维度场景前缀和统一用long long类型;
    • 原数组无需保留时,用原地前缀和优化空间,将空间复杂度降至 O (1);
    • 仅适用于静态数组,需频繁修改元素并查询区间和时,替换为线段树 / 树状数组。
    赞(0)
    未经允许不得转载:171主机测评 » 前缀和(Prefix Sum)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址