Prefix Sums
Authors: Darren Yao, Eric Wei
Prerequisites
Computing range sum queries in constant time over a fixed array.
Focus Problem – read through this problem before continuing!
Resources
Resources: Additional | |||
---|---|---|---|
IUSACO | module is based off this | ||
CPH | rather brief | ||
PAPS | also rather brief |
Introduction
Let's say we have a one-indexed integer array of size and we want to compute the value of
for different pairs satisfying . We'll use the following example with :
Index | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
1 | 6 | 4 | 2 | 5 | 3 |
Naively, for every query, we can iterate through all entries from index to index to add them up. Since we have queries and each query requires a maximum of operations to calculate the sum, our total time complexity is . For most problems of this nature, the constraints will be , so is on the order of . This is not acceptable; it will almost certainly exceed the time limit.
Instead, we can use prefix sums to process these array sum queries. We designate a prefix sum array . First, because we're 1-indexing the array, set , then for indices such that , define the prefix sum array as follows:
Basically, what this means is that the element at index of the prefix sum array stores the sum of all the elements in the original array from index up to . This can be calculated easily in by the following formula for each :
For the example case, our prefix sum array looks like this:
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 1 | 7 | 11 | 13 | 18 | 21 |
Now, when we want to query for the sum of the elements of between (1-indexed) indices and inclusive, we can use the following formula:
Using our definition of the elements in the prefix sum array, we have
Since we are only querying two elements in the prefix sum array, we can calculate subarray sums in per query, which is much better than the per query that we had before. Now, after an preprocessing to calculate the prefix sum array, each of the queries takes time. Thus, our total time complexity is , which should now pass the time limit.
Let's do an example query and find the subarray sum between indices and , inclusive, in the 1-indexed . From looking at the original array, we see that this is
Index | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
1 | 6 | 4 | 2 | 5 | 3 |
Using prefix sums:
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 1 | 7 | 11 | 13 | 18 | 21 |
These are also known as partial sums.
Code
C++
In C++ we can use std::partial_sum
, although it doesn't shorten the code by much.
1#include <bits/stdc++.h>2using namespace std;34#define sz(x) (int)size(x)56using ll = long long;7using vl = vector<ll>;89vl psum(const vl& a) {10 vl psum(sz(a)+1);
Problems
Warning!
The last problem isn't submittable on USACO. Download the test data and use the script provided here to test your solution.
Max Subarray Sum
Now we'll look at some extensions.
Focus Problem – read through this problem before continuing!
This problem has a solution known as Kadane's Algorithm. Please don't use that solution; try to solve it with prefix sums.
Why are the two methods equivalent?
Prefix Minimum, XOR, etc.
Resources: Additional | |||
---|---|---|---|
CPH | defines XOR |
Similar to prefix sums, you can also take prefix minimum or maximum; but you cannot answer min queries over an arbitrary range with prefix minimum. (This is because minimum doesn't have an inverse operation, the way subtraction is to addition.) On the other hand, XOR is its own inverse operation, meaning that the XOR of any number with itself is zero.
Status | Source | Problem Name | Difficulty | Tags | Solution | URL |
---|---|---|---|---|---|---|
Silver | Easy | Show TagsPrefix Sums | External Sol | |||
CSES | Easy | Show TagsPrefix Sums | Show Sketch |
2D Prefix Sums
Focus Problem – read through this problem before continuing!
Now, what if we wanted to process queries for the sum over a subrectangle of a rows by columns matrix in two dimensions? Let's assume both rows and columns are 1-indexed, and we use the following matrix as an example:
0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 5 | 6 | 11 | 8 |
0 | 1 | 7 | 11 | 9 | 4 |
0 | 4 | 6 | 1 | 3 | 2 |
0 | 7 | 5 | 4 | 2 | 3 |
Naively, each sum query would then take time, for a total of . This is too slow.
Let's take the following example region, which we want to sum:
0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 5 | 6 | 11 | 8 |
0 | 1 | 7 | 11 | 9 | 4 |
0 | 4 | 6 | 1 | 3 | 2 |
0 | 7 | 5 | 4 | 2 | 3 |
Manually summing all the cells, we have a submatrix sum of .
The first logical optimization would be to do one-dimensional prefix sums of each row. Then, we'd have the following row-prefix sum matrix. The desired subarray sum of each row in our desired region is simply the green cell minus the red cell in that respective row. We do this for each row to get .
0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 6 | 12 | 23 | 31 |
0 | 1 | 8 | 19 | 28 | 32 |
0 | 4 | 10 | 11 | 14 | 16 |
0 | 7 | 12 | 16 | 18 | 21 |
Now, if we wanted to find a submatrix sum, we could break up the submatrix into a subarray for each row, and then add their sums, which would be calculated using the prefix sums method described earlier. Since the matrix has rows, the time complexity of this is . This might be fast enough for and , but we can do better.
In fact, we can do two-dimensional prefix sums. In our two dimensional prefix sum array, we have
This can be calculated as follows for row index and column index :
The submatrix sum between rows and and columns and , can thus be expressed as follows:
Summing the blue region from above using the 2D prefix sums method, we add the value of the green square, subtract the values of the red squares, and then add the value of the gray square. In this example, we have
as expected.
0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 6 | 12 | 23 | 31 |
0 | 2 | 14 | 31 | 51 | 63 |
0 | 6 | 24 | 42 | 65 | 79 |
0 | 13 | 36 | 58 | 83 | 100 |
Since no matter the size of the submatrix we are summing, we only need to access four values of the 2D prefix sum array, this runs in per query after an preprocessing.
Status | Source | Problem Name | Difficulty | Tags | Solution | URL |
---|---|---|---|---|---|---|
Silver | Easy | Show TagsPrefix Sums | External Sol | |||
Gold | Hard | Show TagsPrefix Sums, Max Subarray Sum | External Sol | |||
Plat | Very Hard | External Sol |
More Complex Applications
Instead of storing just the values themselves, you can also take a prefix sum over , or , for instance. Some math is usually helpful for these problems; don't be afraid to get dirty with algebra!
For instance, we can quickly answer the following type of query:
Find .
First, define the following:
Then, we have:
And so,
Which is what we were looking for!
Status | Source | Problem Name | Difficulty | Tags | Solution | URL |
---|---|---|---|---|---|---|
KS | Easy | Show TagsPrefix Sums | ||||
AC | Hard | Show TagsPrefix Sums | Check AC | |||
Plat | Insane | External Sol |
Module Progress:
Join the USACO Forum!
Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!