【P8218 求区间和】一维前缀和入门完整题解博客
一、题目题意
给定长度为 $n$ 的整数序列,再给出 $m$ 次区间询问,每次给出区间左右端点 $l,r$,输出区间 $[l,r]$ 内所有数字的总和。
样例解读
输入:
4
4 3 2 1
2
1 4
2 3
序列:$[4,3,2,1]$
- 区间 $[1,4]$:$4+3+2+1=10$
- 区间 $[2,3]$:$3+2=5$
输出两行依次为 $10、5$。
二、核心算法:一维前缀和
1. 前缀和定义
定义前缀和数组 $s$,其中 $s[i]$ 代表原数组前 $i$ 项的总和:
$$
s[i] = a_1 + a_2 + \dots + a_i
$$
递推公式:
$$
s[0]=0,\quad s[i] = s[i-1] + a[i]
$$
2. 区间和计算公式
对于任意区间 $[l,r]$,区间总和等于「前 $r$ 项和」减去「前 $l-1$ 项和」:
$$
sum(l,r) = s[r] - s[l-1]
$$
原理:$s[r]$ 包含 $1\sim r$ 全部数字,$s[l-1]$ 包含 $1\sim l-1$ 全部数字,两者相减恰好剩下 $l\sim r$ 的数字之和。

3. 算法优势
- 预处理仅需 $O(n)$ 时间;
- 每次区间查询仅 $O(1)$ 常数时间;
- 对比暴力每次循环累加 $O(n)$ 查询,海量询问下效率提升巨大。
三、完整AC代码
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[100005], s[100005];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
四、分步解题流程
步骤1:输入序列长度 $n$,读入原数组 $a$
循环从 $i=1$ 到 $n$ 读取每个数字,同步计算前缀和数组 $s$:
- $s[0]$ 程序默认初始为0;
- 每一步 $s[i] = s[i-1] + a[i]$,完成预处理。
以样例数据演示前缀和数组生成:
$a[1]=4,\ a[2]=3,\ a[3]=2,\ a[4]=1$
$s[0]=0$
$s[1]=s[0]+4=4$
$s[2]=s[1]+3=7$
$s[3]=s[2]+2=9$
$s[4]=s[3]+1=10$
步骤2:输入询问次数 $m$
循环处理每一组询问,每次读入区间端点 $l、r$。
步骤3:套用公式计算区间和并输出
使用公式 $s[r]-s[l-1]$ 快速得到答案:
- 询问 $1\ 4$:$s[4]-s[0] = 10-0=10$
- 询问 $2\ 3$:$s[3]-s[1] = 9-4=5$
五、代码逐段讲解
-
数组定义
int a[100005], s[100005];数组开到100005,适配题目数据范围;下标从1开始,规避边界 $l=1$ 时 $l-1=0$ 的特殊判断。
-
前缀和预处理循环
for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); s[i] = s[i - 1] + a[i]; }边读入原数组,边递推计算前缀和,一次遍历完成预处理。
-
批量处理区间询问
for (int i = 1; i <= m; i++) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", s[r] - s[l - 1]); }每次查询直接通过前缀和差值算出区间和,无需再次遍历原数组。
六、题型总结
- 适用场景:静态数组(无单点修改)、大量区间求和查询;
- 模板记忆点:前缀和递推 $s[i]=s[i-1]+a[i]$,区间公式 $s[r]-s[l-1]$;
- 拓展延伸:二维前缀和、差分(支持区间修改+单点查询)是该知识点的延伸进阶内容。
浙公网安备 33010602011771号