题目原文
题目描述
输入一个长度为
n
n
n的整数序列。
接下来再输入 m m m个询问,每个询问输入一对 l l l, r r r。
对于每个询问,输出原序列中从第 l l l个数到第 r r r个数的和。
输入格式
第一行包含两个整数
n
n
n和
m
m
m。
第二行包含 n n n个整数,表示整数数列。
接下来 m m m行,每行包含两个整数 l l l和 r r r,表示一个询问的区间范围。
输出格式
共
m
m
m行,每行输出一个询问的结果。
数据范围
1
≤
l
≤
r
≤
n
1≤l≤r≤n
1≤l≤r≤n,
1
≤
n
,
m
≤
100000
1≤n,m≤100000
1≤n,m≤100000,
−
1000
≤
数列中元素的值
≤
1000
−1000≤数列中元素的值≤1000
−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
解题思路
首先,本题可轻易想到暴力解法,即每次输入 l , r l,r l,r都对数组从 a [ l ] a[l] a[l] 累加至 a [ r ] a[r] a[r]。但注意到本题的数据范围,若使用此方法时间复杂度为 O ( m n ) O(mn) O(mn) ,操作次数可能会多达 1010,过于耗时,因此我们想办法改进。
注意到,若已知 a [ i ] a[i] a[i] 数组,我们可在 O ( n ) O(n) O(n) 复杂度求得其前缀和数组 s [ i ] s[i] s[i] 。而在每次操作中,利用公式:
s [ r ] − s [ l − 1 ] = a [ l ] + a [ l + 1 ] + . . . . . . + a [ r − 1 ] + a [ r ] s[r] - s[l - 1] = a[l] + a[l + 1] + ...... + a[r - 1] + a[r] s[r]−s[l−1]=a[l]+a[l+1]+......+a[r−1]+a[r]
即可一次操作求得原序列中从第 l l l 个数到第 r r r 个数的和。改进后的算法最终时间复杂度应为 O ( n + m ) O(n+m) O(n+m)。
代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], s[N];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n ; i ++ )
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
while (m -- )
{
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
后记
提示:在输入输出数据数量很大时用scanf,printf 替换cin,cout 可以减少程序运行时间。
在 ACWing 网站提交本程序后发现程序的运行时间还是比较长,达到了 1004 m s 1004ms 1004ms ,于是将算法中 cin,cout 改成了scanf,printf,发现运行时间明显变短,变为了 168 m s 168ms 168ms。