C++算法·前缀和
前缀和(Prefix(Prefix(Prefix Sum)Sum)Sum)的定义
前缀和是一种高效处理区间求和问题的算法技巧
其核心思想是通过预处理构建一个前缀和数组
使得后续的区间和查询可以在常数时间O(1)O(1)O(1)内完成
核心概念
- 定义
给定一个数组a[1...n]a[1...n]a[1...n],其前缀和数组s[1...n]s[1...n]s[1...n]定义为:
s[0]=0(边界条件)s[0]=0(边界条件)s[0]=0(边界条件)
s[i]=a[1]+a[2]+...+a[i](前i个元素的和)s[i]=a[1]+a[2]+...+a[i](前i个元素的和)s[i]=a[1]+a[2]+...+a[i](前i个元素的和)
通过递推计算:s[i]=s[i−1]+a[i]s[i]=s[i-1]+a[i]s[i]=s[i−1]+a[i] - 作用
快速计算区间和:查询a[l...r]a[l...r]a[l...r]的和时,只需计算s[r]−s[l−1]s[r]-s[l-1]s[r]−s[l−1],无需遍历整个区间。
优化时间复杂度:
预处理:O(n)O(n)O(n)
单次查询:O(1)O(1)O(1)
适用于频繁查询但数据不频繁修改的场景(如静态数组)
对比其他方法差距/使用前缀和的原因
方法 | 预处理时间 | 单次查询时间 | 适用场景 |
---|---|---|---|
暴力遍历 | O(1) | O(n) | 查询极少 |
前缀和 | O(n) | O(1) | 查询多,数据静态 |
线段树/树状数组 | O(n) | O(log n) | 查询和修改都频繁 |
总结
前缀和通过空间换时间,将区间和查询优化至O(1)O(1)O(1),是算法竞赛中的常客
若需支持动态修改,可结合差分数组或升级为树状数组/线段树
前缀和代码模板示例
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],s[N];
int main(){int n,q;//n=数组个数,q=询问次数cin>>n>>q;for(int i=1;i<=n;i++){cin>>a[i];//读入初值s[i]=s[i-1]+a[i];//计算前缀和}for(int i=1;i<=q;i++){int l,r;//询问区间cin>>l>>r;cout<<s[r]-s[l-1]<<endl;//输出区间和}return 0;
}
原理
非常EasyEasyEasy
前缀和数组s[x]s[x]s[x]的值相当于a[1]a[1]a[1]~a[x]a[x]a[x]
利用这个原理计算得知
a[l]到a[r]的和=s[r]−s[l−1]a[l]到a[r]的和=s[r]-s[l-1]a[l]到a[r]的和=s[r]−s[l−1]
例题
P8218 【深进1.例1】求区间和
题目描述
给定由 nnn 个正整数组成的序列 a1,a2,⋯,ana_1, a_2, \cdots, a_na1,a2,⋯,an 和 mmm 个区间 [li,ri][l_i,r_i][li,ri],分别求这 mmm 个区间的区间和。
输入格式
第一行包含一个正整数 nnn,表示序列的长度。
第二行包含 nnn 个正整数 a1,a2,⋯,ana_1,a_2, \cdots ,a_na1,a2,⋯,an。
第三行包含一个正整数 mmm,表示区间的数量。
接下来 mmm 行,每行包含两个正整数 li,ril_i,r_ili,ri,满足 1≤li≤ri≤n1\le l_i\le r_i\le n1≤li≤ri≤n。
输出格式
共 mmm 行,其中第 iii 行包含一个正整数,表示第 iii 组答案的询问。
输入输出样例 #1
输入 #1
4
4 3 2 1
2
1 4
2 3
输出 #1
10
5
说明/提示
样例解释
第 111 到第 444 个数加起来和为 101010。第 222 个数到第 333 个数加起来和为 555。
数据范围
对于 50%50 \%50% 的数据:n,m≤1000n,m\le 1000n,m≤1000;
对于 100%100 \%100% 的数据:1≤n,m≤1051 \le n, m\le 10^51≤n,m≤105,1≤ai≤1041 \le a_i\le 10^41≤ai≤104。
分析
经典的前缀和例题
看输入要求,输入数组个数nnn
然后给数组a[]a[]a[]初值,再输入询问次数mmm
给mmm行的li,ril_i,r_ili,ri并询问a[li]到a[ri]a[l_i]到a[r_i]a[li]到a[ri]的区间和
跟模板一样,但是需要把mmm的输入改到输入数组a[]a[]a[]的后方
ACACAC代码
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],s[N];
int main(){int n,m;//n=数组个数,m=询问次数cin>>n;for(int i=1;i<=n;i++){cin>>a[i];//读入初值s[i]=s[i-1]+a[i];//计算前缀和}cin>>m;for(int i=1;i<=m;i++){int l,r;//询问区间cin>>l>>r;cout<<s[r]-s[l-1]<<endl;//输出区间和}return 0;
}
题单附送
题单题单题单
包含前缀和、差分、离散化