前缀和——(1)什么是前缀和和一维前缀和

本文深入解析了前缀和算法的定义、实现及其在区间求和问题中的应用,通过对比暴力解法,展示了前缀和如何显著提升查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是前缀和

前缀和(Prefix Sum)的定义为:对于一个给定的数列 A, 它的前缀和数列 S 是通过递推能求出来得 S[i] = \sum_{j = 1}^{i}A[j]  部分和。

例如:

A[5, 6, 7, 8] = PrefixSum[5, 11, 18, 26]\\ PrefixSum[0] = A[0]\\ PrefixSum[1] = A[0] + A[1]\\ PrefixSum[2] = A[0] + A[1] + A[2]\\ PrefixSum[3] = A[0] + A[1] + A[2] + A[3]\\ ...\\ PrefixSum[n] = A[0] + A[1] + A[2] + A[3] + ... + A[n]

C++实现

//假设数组a和前缀和数组s都已经定义
int i;
//初始条件
a[0] = 0;
s[0] = 0;
for (i=1; i<=n; i++) {
    cin >> a[i];
    s[i] = s[i-1] + a[i];
}

模板题

下面我们用一个模板题,将完整的一维数组前缀和做一个简单的展示。题目链接http://47.110.135.197/problem.php?id=5181

#include <bits/stdc++.h>

using namespace std;

int main() {
	int n;
	cin >> n;
	
	int data;
	int ans[102] = {};
	for (int i=1; i<=n; i++) {
		cin >> data;
		ans[i] = ans[i-1] + data;
	}
	
	for (int i=1; i<=n; i++) {
		cout << ans[i] << " ";
	}
	cout << endl;
	
	return 0;
}

用途

前缀和是一种重要的预处理,能大大降低查询的时间复杂度。

前缀和是以求和的方式灵活地面对区间询问。

下面我们用一个模板题来说明。

例题:区间求和

给你一串长度为 n 的数列 a1, a2, a3, ..., an,再给出 m 个询问,每次询问给出 L, R 两个数,要求给出区间 [L, R] 里的数的和。

详细可以参看http://47.110.135.197/problem.php?id=5139

题目分析

题目非常简单,我们也可以得到一个最简单的解法,暴力操作。也就是对应每个询问,我们都从 L 开始到 R 结束对这个区间的数据进行求和。基本的代码如下:

#include <iostream>

using namespace std;

const int MAXN = 1e5+2;
long long arr[MAXN] = {};

int main() {
    int n;
    cin >> n;
    
    int i, j;
    for (i=1; i<=n; i++) {
        cin >> arr[i];
    }

    int m;
    int l, r;
    long long ans = 0;
    cin >> m;
    for (i=0; i<m; i++) {
        cin >> l >> r;

        ans = 0;
        for (j=l; j<=r; j++) {
            ans += arr[j];
        }

        cout << ans << endl;
    }

    return 0;
}

代码分析

从上面的代码非常明确的分析出来,代码的时间复杂度为 O(n * m)。也就是说,在比赛中这样的解法能否 AC,那就要看数据的量了。当然我们都知道这样的解法,比赛中是肯定不可能 AC。因此唯一的可能就是降低时间复杂度。方法就是使用前缀和。

我们先看前缀和的数学。数列A中某个下标区间 [l, r] 内的数的和定义为:

sum[l, r] = \sum_{i = l}^{r}A[i]\\ = a[l] + a[l+1] + \cdots + a[r]\\ = S[r] - S[l-1]

从上面推导,我们可以清晰的看出,前缀和和区间和的关系。

代码改进

#include <iostream>

using namespace std;

const int MAXN = 1e5+2;
long long arr[MAXN] = {};
long long sum[MAXN] = {};

int main() {
    int n;
    cin >> n;
    
    int i, j;
    for (i=1; i<=n; i++) {
        cin >> arr[i];
        sum[i] = sum[i-1] + arr[i];
    }

    int m;
    int l, r;
    cin >> m;
    for (i=0; i<m; i++) {
        cin >> l >> r;
        cout << sum[r] - sum[l-1] << endl;
    }

    return 0;
}

代码分析

从上面的代码非常明确的分析出来,代码的时间复杂度为 O(n)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值