Rust for Algorithmic Problem-Solving

Prefix Sums

A prefix sum array (and its dual, the difference array) precomputes cumulative sums in $O(n)$ time so that the sum of any contiguous range [left..right] (or 2D sub-rectangle) can be answered in $O(1)$ constant time.

1D Prefix Sum with Leading Zero (`prefix.len() == arr.len() + 1`):
  Index:      0    1    2    3    4    5
  `arr`:         [ 10 | 20 |  5 | 30 | 15 ]
  `prefix`: [  0 | 10 | 30 | 35 | 65 | 80 ]
  Range Sum `arr[1..4]` (20 + 5 + 30)  ===  `prefix[4] - prefix[1]` (65 - 10 = 55) in O(1)!

Why Always Include a Leading 0 at prefix[0]

Building prefix of length n + 1 with prefix[0] = 0 and prefix[i + 1] = prefix[i] + arr[i] eliminates every if left == 0 boundary branch: the sum of the half-open slice arr[left..right] is unconditionally prefix[right] - prefix[left] for all 0 <= left <= right <= n!

Technique Precomputation Cost Range Query Cost Range Update Cost Best Used When
1D Prefix Sum $O(n)$ $O(1)$ $O(n)$ Many range sum queries on a static array.
2D Prefix Sum $O(R \cdot C)$ $O(1)$ $O(R \cdot C)$ Many 2D sub-grid sum queries on a static matrix.
Difference Array $O(1)$ per range add $O(n)$ final reconstruction $O(1)$ (diff[l] += v; diff[r] -= v;) Applying $Q$ offline range additions [l..r] += delta before reading the final array!

1D Prefix Sum, 2D Prefix Sum, and Difference Arrays in Rust

/// 1D Prefix Sum supporting O(1) half-open range queries `[left..right]`.
pub struct PrefixSum1D {
    prefix: Vec<i64>,
}

impl PrefixSum1D {
    pub fn new(arr: &[i64]) -> Self {
        let mut prefix = Vec::with_capacity(arr.len() + 1);
        prefix.push(0);
        let mut acc = 0i64;
        for &x in arr {
            acc += x;
            prefix.push(acc);
        }
        Self { prefix }
    }

    /// Returns the sum of `arr[left..right]` in O(1) time.
    #[inline]
    pub fn query(&self, left: usize, right: usize) -> i64 {
        self.prefix[right] - self.prefix[left]
    }
}

/// 2D Prefix Sum supporting O(1) sub-rectangle queries `[r1..r2, c1..c2]`.
pub struct PrefixSum2D {
    cols: usize,
    pref: Vec<i64>,
}

impl PrefixSum2D {
    pub fn new(grid: &[Vec<i64>]) -> Self {
        let r_len = grid.len();
        let c_len = grid.first().map_or(0, Vec::len);
        let stride = c_len + 1;
        let mut pref = vec![0i64; (r_len + 1) * stride];

        for r in 0..r_len {
            for c in 0..c_len {
                pref[(r + 1) * stride + (c + 1)] = grid[r][c]
                    + pref[r * stride + (c + 1)]
                    + pref[(r + 1) * stride + c]
                    - pref[r * stride + c];
            }
        }
        Self { cols: stride, pref }
    }

    /// Returns the sum of sub-grid `rows [r1..r2)`, `cols [c1..c2)` in O(1) time!
    #[inline]
    pub fn query_rect(&self, r1: usize, c1: usize, r2: usize, c2: usize) -> i64 {
        let s = self.cols;
        self.pref[r2 * s + c2] - self.pref[r1 * s + c2] - self.pref[r2 * s + c1]
            + self.pref[r1 * s + c1]
    }
}

/// Applies `updates` of the form `(left, right_exclusive, delta)` in O(N + Q) total time
/// using a Difference Array.
pub fn apply_range_additions(len: usize, updates: &[(usize, usize, i64)]) -> Vec<i64> {
    let mut diff = vec![0i64; len + 1];
    for &(l, r, delta) in updates {
        diff[l] += delta;
        diff[r] -= delta;
    }

    let mut out = Vec::with_capacity(len);
    let mut running = 0i64;
    for &d in &diff[..len] {
        running += d;
        out.push(running);
    }
    out
}

fn main() {
    let p1 = PrefixSum1D::new(&[10, 20, 5, 30, 15]);
    assert_eq!(p1.query(1, 4), 55);

    let grid = vec![
        vec![1, 2, 3],
        vec![4, 5, 6],
        vec![7, 8, 9],
    ];
    let p2 = PrefixSum2D::new(&grid);
    // Sub-grid rows 1..3, cols 1..3 -> 5 + 6 + 8 + 9 = 28
    assert_eq!(p2.query_rect(1, 1, 3, 3), 28);

    let updated = apply_range_additions(5, &[(1, 4, 10), (2, 5, 5)]);
    assert_eq!(updated, vec![0, 10, 15, 15, 5]);

    println!("1D sum: {}, 2D rect sum: {}, Diff array: {updated:?}", p1.query(1, 4), p2.query_rect(1, 1, 3, 3));
}
1D sum: 55, 2D rect sum: 28, Diff array: [0, 10, 15, 15, 5]