【模板】静态区间和(前缀和)
思路
拿到这道题,先想一下:给你一个数组,反复查询某一段区间的和,暴力做法每次查询都遍历一遍区间,时间复杂度 ,数据量
级别直接超时。有没有办法把每次查询优化到
?
这就是前缀和的经典应用场景了。
什么是前缀和?
前缀和数组 pre[i] 表示原数组前 i 个元素的累加和。构建方式很简单:
$$
其中 ,作为边界。
怎么用前缀和求区间和?
想求区间 的和,直接用:
$$
为什么?你画个图就明白了: 是前
个元素的总和,
是前
个元素的总和,两者一减,剩下的不就是第
到第
个元素的和吗?
有什么坑?
这题元素范围是 ,数组长度最大
,前缀和最大能到
级别,int 会爆,必须用
long long。
代码
#include <cstdio>
using namespace std;
long long pre[1000001];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
long long x;
scanf("%lld", &x);
pre[i] = pre[i - 1] + x;
}
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%lld\n", pre[r] - pre[l - 1]);
}
return 0;
}
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
long[] pre = new long[n + 1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + Long.parseLong(st.nextToken());
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int l = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
sb.append(pre[r] - pre[l - 1]).append('\n');
}
System.out.print(sb);
}
}
import sys
input = sys.stdin.readline
def main():
n, m = map(int, input().split())
a = list(map(int, input().split()))
pre = [0] * (n + 1)
for i in range(n):
pre[i + 1] = pre[i] + a[i]
out = []
for _ in range(m):
l, r = map(int, input().split())
out.append(str(pre[r] - pre[l - 1]))
sys.stdout.write('\n'.join(out) + '\n')
main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
let idx = 0;
const [n, m] = lines[idx++].split(' ').map(Number);
const a = lines[idx++].split(' ').map(Number);
const pre = new Array(n + 1).fill(0);
for (let i = 0; i < n; i++) {
pre[i + 1] = pre[i] + a[i];
}
const out = [];
for (let i = 0; i < m; i++) {
const [l, r] = lines[idx++].split(' ').map(Number);
out.push(pre[r] - pre[l - 1]);
}
console.log(out.join('\n'));
});
复杂度分析
- 时间复杂度:
,预处理前缀和
,每次查询
,共
次查询
- 空间复杂度:
,存储前缀和数组
总结
前缀和是处理静态区间查询的基本功,核心就两步:建前缀和数组、用减法算区间和。这题本身不难,但一定要注意数据类型用 long long,不然就是白送的 WA。Java 那边注意用 BufferedReader 读入,Scanner 在大数据量下容易超时。



京公网安备 11010502036488号