Difference arrays

A difference array applies many changes of the form "add x to every element from l to r" with two small writes each, and one running total at the end gives the final array. If the values must be read between the changes, use a Fenwick tree instead.

Explanation

An array has 5 elements, all 0, and 3 must be added to elements 2 to 4. In a second row, write +3 under element 2 and −3 under element 5, then take the running total of that row (each cell plus everything before it) from left to right: 0, 3, 3, 3, 0. The −3 is what stops the change: without it, the running total would carry the 3 on into element 5.

Two rows of cells: the marks row gets +3 under element 2 and −3 under element 5, then a running total sweeps left to right and fills the five elements with 0, 3, 3, 3, 0.

Fig. 1. +3 under element 2 and −3 under element 5 add 3 to elements 2 to 4.

Real problems have many changes, and they overlap. A bike shop rents out bikes for 8 days. There are four rentals: 3 bikes on days 2 to 5, 2 bikes on days 4 to 8, 1 bike on days 1 to 3, and 4 bikes on days 6 to 7. How many bikes are out on each day? Writing each rental into every day it covers takes 4 + 5 + 3 + 2 = 14 additions here. With 10^6 days and 10^6 rentals that can be 10^{12} additions, far too slow.

A running total carries each number forward to every later day, so each rental needs only two marks: its number on its first day, and minus its number on the first day it no longer covers. Rental 1 gets +3 on day 2 and −3 on day 6.

This row of marks is the difference array. Using it takes two steps: mark each change with +x at l and −x at r + 1, then build the final values with one running total.

Four rentals drawn as rows over days 1 to 8; below them a row of marks for cells 1 to 9 and a row of running totals, filled in step by step to 1, 4, 4, 5, 5, 6, 6, 2.

Before any rental is marked, every mark is 0.

1/13

Fig. 2. Step through the marks and the running total with the buttons or the arrow keys. Blue +x marks add a rental, red −x marks take it away, and the outlined cells are the ones being added.

The build gave every day's count at once, 1, 4, 4, 5, 5, 6, 6, 2, with 8 marks and one pass over the days instead of 14 additions.

Each mark is a day's count minus the count of the day before: day 6 has 6 bikes, day 5 has 5, and the mark on day 6 is 1. So if the array doesn't start at zeros, first read it into marks, each one the element minus the element before it. The array 4, 4, 7, 7, 7, 2 gives marks 4, 0, 3, 0, 0, −5. Adding 1 to elements 2 to 3 makes them 4, 1, 3, −1, 0, −5, and the running total gives 4, 5, 8, 7, 7, 2.

Three rows of cells: the starting array 4, 4, 7, 7, 7, 2; under it the marks after adding 1 to elements 2 to 3, which are 4, 1, 3, −1, 0, −5 and a grey 0 past the end, with the +1 on cell 2 shaded blue and the −1 on cell 4 shaded red; and at the bottom the final values 4, 5, 8, 7, 7, 2, with elements 2 and 3 outlined.

Fig. 3. The change touches only cells 2 and 4; every other mark keeps the difference it started with.

Reach for a difference array when a problem has many changes to ranges of consecutive elements, and the values are read only after all of them.

In C++

The elements are a[1] to a[n], with a[0] = 0. Rental 1 above becomes d[2] += 3 and d[6] -= 3.

vector<long long> d(n + 2, 0);   // n + 2 cells: the mark at r + 1 can land at n + 1;
                                 // with n + 1 cells it writes past the end

// read
for (int i = 1; i <= n; i++)
    d[i] = a[i] - a[i - 1];      // differences of the starting array (all 0 if it starts at zeros)

// mark: add x to elements l to r
d[l] += x;
d[r + 1] -= x;

// build
for (int i = 1; i <= n; i++)
    a[i] = a[i - 1] + d[i];

The range must have l ≤ r. If the input may give the ends in either order, swap them first: with l = 4, r = 2 the marks would subtract 1 from element 3.

Each change takes and the build takes , so k changes take O(n + k) time and memory.

Cover counts and the busiest point

When every change adds 1, or the size of a booking, the built values count what covers each element, and the largest one is the busiest element. On the rentals, the largest value is 6 bikes, on days 6 and 7. Keeping the maximum while building gives it.

// mark: each interval adds x (x = 1 to count intervals)
for (int j = 0; j < k; j++) {
    int l, r;
    long long x;
    cin >> l >> r >> x;
    d[l] += x;
    d[r + 1] -= x;
}

// build, keeping the largest value
long long best = 0;
for (int i = 1; i <= n; i++) {
    a[i] = a[i - 1] + d[i];
    best = max(best, a[i]);
}

If an interval is given as [l, r), so that it doesn't cover r, the −x mark goes at r. If times start at 0, add 1 to every time. To count the elements covered at least c times, replace best = max(best, a[i]) with if (a[i] >= c) cnt++.

Common mistakes

  • Forgetting the −x mark keeps a change in the total forever. With only first-day marks on the rentals, the totals are 1, 4, 4, 6, 6, 10, 10, 10, so day 6 shows 10 instead of 6. Every change needs both marks.

Similar techniques

Prefix sums answer range-sum questions about an array that doesn't change.

2D prefix sums apply changes to rectangles of a table with four marks each.

When the questions ask for range sums or minimums between range changes, use a segment tree with lazy propagation. For questions about one element, the Fenwick tree from the question above is enough.

A sweep line uses the same marks when coordinates reach 10^9 and no array fits: it sorts the marks in O(k \log k) and walks them in order.

Practice

Was this page helpful?