Prefix sums
In this article, we are going to focus on the following problem (or variations of it):
You are given an array of length , along with queries. In each query, you are given a range and want to compute the sum (referred to as sum(l, r) from now on). How do we accomplish this in time per query?
You are encouraged to try tackling this problem on your own! It’s a subproblem that will come up again and again while you’re doing other problems.
The naive solution is to iterate over all indices from to , and add each value one by one. However, as each query takes a maximum of time, our total time complexity will be . In the problem provided above, this means operations — way too slow!
Instead, let’s approach the problem from another angle. You might have noticed how there’s a lot of redundant information that we compute over and over again. Assuming we already know sum(l, a) and sum(l, b) with , we can already figure out that
sum(l, a) is included in both sums, so they cancel out — leaving behind sum(a+1, b). But and could be any number, so if we could choose and precompute sum(1, r) for each value of r, we would be able to answer each query in time!
That might have been a bit too fast, so let’s go over an example together.
Assume the array is as the following, with :
Then, the prefix sum array will be as follows:
Now, if we want to calculate sum(2, 4)
We can just use !
Implementation
Notice how you can’t build the prefix sum array naively by summing each prefix over and over again (that would take time). Instead, observe how ; thanks to this, we can build our prefix sum array in total.
In addition, using 1-based indexing with simplifies building and querying the prefix sum array.
Here’s an example of how to implement this in C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, Q;
cin >> N >> Q;
vector<int> A(N + 1);
for(int i = 1; i <= N; ++i) {
cin >> A[i];
}
vector<long long> P(N + 1); // beware of overflow!
for(int i = 1; i <= N; ++i) {
P[i] = P[i - 1] + A[i]; // build the prefix sum array
}
for(int query_num = 0; query_num < Q; ++query_num) {
int l, r;
cin >> l >> r;
cout << P[r] - P[l - 1] << "\n"; // queries are very simple!
}
}You can also use std::partial_sum to build the prefix sum array. Be careful about overflow though — it uses the value type pointed to by the input iterator, so A being a vector of integers might present issues.