计算区间和_题解

【题解提供者】吴立强

解法【1】

思路

直接统计每次询问的答案 ∑ i = l i r i A i \sum_{i=l_i}^{r_i} A_i i=liriAi 即可。

代码展示

#include 
using namespace std;

const int N = 1009;
int a[N];

int main() {
  int n;  cin >> n;
  for(int i = 1; i <= n; i ++) cin >> a[i];
  int m;  cin >> m;
  while(m --) {
    int l, r;  cin >> l >> r;
    int sum = 0;
    for(int i = l; i <= r; i ++) sum += a[i];
    cout << sum << endl;
  }
  return 0;
}

算法分析

不难发现,上述程序的循环内所需运行次数为 n + ∑ i = 1 m ( r i − l i + 1 ) n+\sum_{i=1}^m (r_i-l_i+1) n+i=1m(rili+1),在极限数据(所有查询都是从 1 到 n)其级别为 O ( n × m ) O(n\times m) O(n×m) 可以通过 1000 的数据,而在 2 × 1 0 5 2\times 10^5 2×105 的数据下会 TLE。

解法【2】

思路

定义 s u m i sum_i sumi 为数组中前 i i i 个元素的和。

那么有 s u m i = s u m i − 1 + A i sum_i = sum_{i-1}+A_i sumi=sumi1+Ai,特别的, s u m 0 = 0 sum_0 = 0 sum0=0

通过线性时间处理出上述数据后,有: ∑ i = l i r i A i = s u m r i − s u m l i − 1 \sum_{i={l_i}}^{r_i}A_i = sum_{r_i} - sum_{l_i-1} i=liriAi=sumrisumli1

代码展示

#include 
using namespace std;
typedef long long ll;  /// 类型定义(用 ll 来代替了 long long 的功能)

const int N = 200009;
int a[N];
ll sum[N];  /// 极限数据下 sum[n] 可以达到 2e10 的级别超出 int 的表达范围

int main() {
  int n;  cin >> n;
  for(int i = 1; i <= n; i ++) {
  	cin >> a[i];
  	sum[i] = sum[i - 1] + a[i];  /// 预处理,由于是全局数组,故存在 sum[0]=0
  }
  int m;  cin >> m;
  while(m --) {
    int l, r;  cin >> l >> r;
    cout << sum[r] - sum[l - 1] << endl;  /// O(1) 回答
  }
  return 0;
}

算法分析

程序时间复杂度为 O ( n + m ) O(n+m) O(n+m),可以通过 2 × 1 0 5 2\times 10^5 2×105 的数据。

算法拓展

上述算法为【前缀和】算法,其可以将区间和查询从区间长度优化至两次单点查询。

这揭示了一种解题思想:区间个数是 O ( n 2 ) O(n^2) O(n2) 级别的,但是利用【前缀】这个信息可以快速维护出区间的信息,更重要的是【前缀】只有 n + 1 n+1 n+1 个(还有一个空前缀),且前缀之间存在递推关系,故通过 O ( n ) O(n) O(n) 级别的预处理后就可以加速区间查询的速度。

你可能感兴趣的:(算法,c++,数据结构)