rmq/
lib.rs

1#[inline(always)]
2fn flog2(v: usize) -> usize {
3    v.ilog2() as usize
4}
5
6pub trait Rmq {
7    fn rmq(&self, i: usize, j: usize) -> Option<usize>;
8}
9
10pub struct Sparse {
11    lgn: usize,
12    table: Vec<usize>,
13}
14
15impl Sparse {
16    pub fn new(array: &[usize]) -> Self {
17        let n = array.len();
18        let lgn = flog2(n) + 1;
19        let mut table: Vec<usize> = vec![0; lgn * n];
20
21        for j in 0..n {
22            table[j * lgn] = array[j];
23        }
24
25        for i in 1..lgn {
26            for j in 0..=(n - (1 << i)) {
27                table[j * lgn + i] = std::cmp::min(
28                    table[j * lgn + i - 1],
29                    table[(j + (1 << (i - 1))) * lgn + i - 1],
30                );
31            }
32        }
33
34        Sparse { lgn, table }
35    }
36}
37
38impl Rmq for Sparse {
39    fn rmq(&self, i: usize, j: usize) -> Option<usize> {
40        if i >= j {
41            return None;
42        }
43
44        let k = flog2(j - i);
45        Some(std::cmp::min(
46            self.table[i * self.lgn + k],
47            self.table[(j - (1 << k)) * self.lgn + k],
48        ))
49    }
50}
51
52/// 2D Array class
53struct Matrix {
54    n: usize,
55    table: Vec<usize>,
56}
57
58impl Matrix {
59    pub fn new(n: usize) -> Matrix {
60        let table = vec![0; n * n];
61        Matrix { n, table }
62    }
63
64    fn flat_index(&self, i: usize, j: usize) -> usize {
65        debug_assert!(i < self.n && j < self.n);
66        i * self.n + j
67    }
68}
69
70impl std::ops::Index<(usize, usize)> for Matrix {
71    type Output = usize;
72    fn index(&self, index: (usize, usize)) -> &Self::Output {
73        let (i, j) = index;
74        &self.table[self.flat_index(i, j)]
75    }
76}
77
78impl std::ops::IndexMut<(usize, usize)> for Matrix {
79    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
80        let (i, j) = index;
81        let flat_idx = self.flat_index(i, j);
82        &mut self.table[flat_idx]
83    }
84}
85
86/// Upper triangular table: Table for looking up at [i,j) (j > i) intervals.
87#[derive(Clone)]
88struct UTTable {
89    n: usize,
90    table: Vec<usize>,
91}
92
93impl UTTable {
94    pub fn new(n: usize) -> UTTable {
95        let table: Vec<usize> = vec![0; n * (n + 1) / 2];
96        UTTable { n, table }
97    }
98
99    fn flat_index(&self, i: usize, j: usize) -> usize {
100        debug_assert!(i < self.n);
101        debug_assert!(i < j && j <= self.n);
102        let k = self.n - i - 1;
103        k * (k + 1) / 2 + j - i - 1
104    }
105}
106
107impl std::ops::Index<(usize, usize)> for UTTable {
108    type Output = usize;
109    fn index(&self, index: (usize, usize)) -> &Self::Output {
110        let (i, j) = index;
111        &self.table[self.flat_index(i, j)]
112    }
113}
114
115impl std::ops::IndexMut<(usize, usize)> for UTTable {
116    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
117        let (i, j) = index;
118        let flat_idx = self.flat_index(i, j);
119        &mut self.table[flat_idx]
120    }
121}
122
123/// Fully tabulating the answer to all queries with <O(n²),O(1)>
124#[derive(Clone)]
125struct TabulatedQuery {
126    tbl: UTTable,
127}
128
129impl Rmq for TabulatedQuery {
130    fn rmq(&self, i: usize, j: usize) -> Option<usize> {
131        if i < j {
132            Some(self.tbl[(i, j)])
133        } else {
134            None
135        }
136    }
137}
138
139impl TabulatedQuery {
140    pub fn new(x: &[usize]) -> Self {
141        let mut tbl = UTTable::new(x.len());
142        for i in 0..x.len() {
143            tbl[(i, i + 1)] = i;
144        }
145        for i in 0..x.len() - 1 {
146            for j in i + 2..x.len() + 1 {
147                // DP: min val in [i,j) is either min in [i,j-1) or [j-1,j)
148                tbl[(i, j)] = if x[tbl[(i, j - 1)]] <= x[j - 1] {
149                    tbl[(i, j - 1)]
150                } else {
151                    j - 1
152                };
153            }
154        }
155        TabulatedQuery { tbl }
156    }
157}
158
159#[derive(Clone, Copy)]
160struct BlockSize(pub usize);
161
162#[derive(PartialEq, PartialOrd)]
163struct BlockIdx(pub usize);
164
165fn block_size(n: usize) -> BlockSize {
166    BlockSize(flog2(n) + 1)
167}
168
169fn round_down(i: usize, bs: BlockSize) -> (BlockIdx, usize) {
170    let BlockSize(bs) = bs;
171    let r = i / bs;
172    (BlockIdx(r), r * bs)
173}
174fn round_up(i: usize, bs: BlockSize) -> (BlockIdx, usize) {
175    let BlockSize(bs) = bs;
176    let r = (i + bs - 1) / bs;
177    (BlockIdx(r), r * bs)
178}
179
180/// Reduce an array x to the smallest value in each block (of size block_size)
181/// and the index in the original array that this minimal value sits at.
182fn reduce_array(x: &[usize], block_size: BlockSize) -> Vec<usize> {
183    let BlockSize(bs) = block_size;
184    x.chunks(bs)
185        .map(|block| *block.iter().min().unwrap())
186        .collect()
187}
188
189/// Build a table of Ballot numbers B_pq from p=q=0 to p=q=b.
190/// B_bb is the total number of binary trees with b leaves.
191fn tabulate_ballot_numbers(b: usize) -> Matrix {
192    let mut ballot = Matrix::new(b + 1);
193    for q in 0..=b {
194        ballot[(0, q)] = 1
195    }
196    for q in 1..=b {
197        for p in 1..=q {
198            ballot[(p, q)] = ballot[(p - 1, q)] + ballot[(p, q - 1)]
199        }
200    }
201    ballot
202}
203
204/// Compute the block type number of a block.
205/// The b argument is the true block size, but it can differ from block.len() for the last
206/// block. When we process the last block, we fake push the missing elements, putting them
207/// lower in the Cartesian tree than the real ones, so we still get the right RMQ.
208fn block_type(block: &[usize], b: usize, stack: &mut [i64], ballot: &Matrix) -> usize {
209    let mut num = 0;
210    let mut top = 0;
211    stack[top] = i64::MIN; // As close to -infinity as we get with this type...
212
213    for (i, &v) in block.iter().enumerate() {
214        let signed_v = v as i64;
215
216        // Invariant: When i runs from zero to b, b-i is p in B[p,q].
217        //            i-top is how the depth of the stack and b-(i-top)
218        //            is then the q in B[p,q].
219        let p = b - i;
220        while stack[top] > signed_v {
221            // Popping
222            let q = b - (i - top);
223            num += ballot[(p - 1, q)];
224            top -= 1;
225        }
226
227        // Push...
228        top += 1;
229        stack[top] = signed_v;
230    }
231
232    num
233}
234
235/// Compute the block types and the tables for the blocks we observe in x.
236fn tabulate_blocks(x: &[usize], b: usize) -> (Vec<usize>, Vec<Option<TabulatedQuery>>) {
237    // We need to round up to get the number of blocks here.
238    // The reduced array handles blocks 0, 1, ..., x.len()/b but we
239    // might also have a block after it.
240    let no_blocks = (x.len() + b - 1) / b;
241
242    let ballot = tabulate_ballot_numbers(b);
243    let mut stack: Vec<i64> = vec![i64::MIN; b + 1];
244
245    let mut block_types = vec![0; no_blocks];
246    let mut block_tables = vec![None; ballot[(b, b)]];
247    for i in 0..no_blocks {
248        let begin = i * b;
249        // The last block might be smaller than a full block, but if we just
250        // tabulate it anyway the missing values are virtually pushed and behave
251        // like they are larger than the existing ones, giving us the right RMQ
252        // results anyway (the true values are always smaller than the virtual ones).
253        let end = std::cmp::min(x.len(), begin + b);
254        let block = &x[begin..end];
255
256        let bt = block_type(block, b, &mut stack, &ballot);
257        block_types[i] = bt;
258        if block_tables[bt].is_none() {
259            block_tables[bt] = Some(TabulatedQuery::new(block));
260        }
261    }
262    (block_types, block_tables)
263}
264
265pub struct Tabulation<'a> {
266    x: &'a [usize],
267    block_size: BlockSize,
268    sparse: Sparse,
269    block_types: Vec<usize>,
270    block_tables: Vec<Option<TabulatedQuery>>,
271}
272
273impl<'a> Tabulation<'a> {
274    pub fn new(x: &'a [usize]) -> Self {
275        let n = x.len();
276        let BlockSize(b) = block_size(n);
277
278        // adjust block size; log(n) is too much so change it to a quarter.
279        let b = std::cmp::max(4, b / 4); // I don't want too small blocks, so minimum is 4
280        let block_size = BlockSize(b);
281
282        let reduced_vals = reduce_array(x, block_size);
283        let (block_types, block_tables) = tabulate_blocks(x, b);
284
285        Tabulation {
286            x,
287            block_size,
288            sparse: Sparse::new(&reduced_vals),
289            block_types,
290            block_tables,
291        }
292    }
293
294    fn block_rmq(&self, i: usize, j: usize) -> Option<usize> {
295        if j <= i {
296            return None;
297        }
298
299        let BlockSize(bs) = self.block_size;
300        let block_index = i / bs; // The index in the list of blocks
301        let block_begin = block_index * bs; // The index the block starts at in x
302
303        // Get the table for this block by looking up the block type...
304        let btype = self.block_types[block_index];
305        // ... and then the table from the block type.
306        let tbl = self.block_tables[btype].as_ref().unwrap();
307
308        // Get RMQ and adjust the index back up, so it is relative to the start of the block.
309        let rmq_idx = tbl.rmq(i - block_begin, j - block_begin)? + block_begin;
310        Some(self.x[rmq_idx])
311    }
312}
313
314fn lift_op<T: Copy>(f: impl Fn(T, T) -> T) -> impl Fn(Option<T>, Option<T>) -> Option<T> {
315    move |a, b| match (a, b) {
316        (None, None) => None,
317        (Some(_), None) => a,
318        (None, Some(_)) => b,
319        (Some(a), Some(b)) => Some(f(a, b)),
320    }
321}
322
323impl<'a> Rmq for Tabulation<'a> {
324    fn rmq(&self, i: usize, j: usize) -> Option<usize> {
325        let BlockSize(bs) = self.block_size;
326        let bi = BlockIdx(i / bs);
327        // The block indices are not the same for the small tables and the sparse table.
328        // For the sparse table we have to round up for i ...
329        let (sparse_bi, ii) = round_up(i, BlockSize(bs));
330        // ... but to get the block i is in, we need to round down.
331        let (bj, jj) = round_down(j, BlockSize(bs));
332
333        if bi < bj {
334            let p1 = self.block_rmq(i, ii);
335            let (BlockIdx(sparse_bi), BlockIdx(bj)) = (sparse_bi, bj);
336            let p2 = self.sparse.rmq(sparse_bi, bj);
337            let p3 = self.block_rmq(jj, j);
338            let min = lift_op(std::cmp::min);
339            Some(min(min(p1, p2), p3)?)
340        } else {
341            Some(self.block_rmq(i, j)?)
342        }
343    }
344}
345
346#[cfg(test)]
347mod tests;