问题背景
在处理数组区间查询类问题时,我们常会遇到这样的场景:给定一个长度为 n 的数组,随后进行 m 次查询操作。每次查询给出两个参数 l 和 r,要求输出数组中第 l 到第 r 个元素之和。
如果直接对每次查询遍历数组累加,单次查询的时间复杂度是 O(n),总复杂度会达到 O(m*n)。当 n 和 m 都达到 10^5 级别时,这种暴力解法必然超时。这时候,前缀和(Prefix Sum)就是解决这类问题的标准方案。
核心思路
前缀和的核心思想是用空间换时间。我们预先计算好每个位置的前缀和,存入一个新的数组中。这样在查询任意区间 [l, r] 的和时,就可以通过两次减法运算直接得出结果,将单次查询复杂度降为 O(1)。
假设原数组为 arr,前缀和数组为 dp。定义 dp[i] 表示从数组开头到第 i 个元素的累积和。状态转移方程非常简单:
dp[i] = dp[i - 1] + arr[i]
有了这个预处理数组,区间 [l, r] 的和就等于 dp[r] - dp[l - 1]。这里需要注意下标处理,通常为了方便计算,我们会让数组下标从 1 开始,这样 dp[0] 默认为 0,边界条件更清晰。
另外,由于元素值可能较大且累加次数多,求和结果很容易超出 32 位整数范围,因此在代码实现中建议使用 long 类型存储前缀和数组。
代码实现
下面是一个基于 Java 的完整实现示例。为了适应输入规模,使用了 Scanner 读取数据,并重点展示了如何构建前缀和数组以及如何进行 O(1) 查询。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 读取数组长度 n 和查询次数 m
int n = in.nextInt();
int m = in.nextInt();
// 使用长整型数组存储前缀和,防止溢出
// 下标从 1 开始,方便处理 l-1 的情况
long[] dp = new long[n + 1];
// 读取原始数组并同步计算前缀和
for (int ; i <= n; i++) {
in.nextInt();
dp[i] = dp[i - ] + val;
}
(m > ) {
in.nextInt();
in.nextInt();
System.out.println(dp[r] - dp[l - ]);
m--;
}
in.close();
}
}


