diff --git a/Cargo.toml b/Cargo.toml index e3312027..e0b278d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -197,8 +197,16 @@ name = "lis_yosupo" path = "examples/helpers/lis_yosupo.rs" [[example]] -name = "lis_pop" -path = "examples/helpers/lis_pop.rs" +name = "lis_handmade" +path = "examples/helpers/lis_handmade.rs" + +[[example]] +name = "range_container_aizu" +path = "examples/data_structures/range_container_aizu.rs" + +[[example]] +name = "range_container_handmade" +path = "examples/data_structures/range_container_handmade.rs" [[example]] name = "mono_st" @@ -222,4 +230,8 @@ path = "examples/data_structures/disjoint_rmq_non_commutative.rs" [[example]] name = "lca_rmq_next_on_path" -path = "examples/graphs/lca_rmq_next_on_path.rs" \ No newline at end of file +path = "examples/graphs/lca_rmq_next_on_path.rs" + +[[example]] +name = "linear_rmq" +path = "examples/data_structures/linear_rmq.rs" diff --git a/examples/README.md b/examples/README.md index d218d77c..5c4e1ae0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,6 +2,7 @@ - tests are named `[algo].rs` - use both yosupo and aizu to test whenever possible because bugs have existed on one of the sites but not the other - when using both sites name the files `[algo]_yosupo.rs` and `[algo]_aizu.rs` +- when there's no problem to test on, test on [hello world](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A) and name the files `[algo]_handmade.rs` - when only testing a specific function or componenet of some algorithm name the file `[algo]_[component].rs` # Documentation Guidelines diff --git a/examples/data_structures/linear_rmq.rs b/examples/data_structures/linear_rmq.rs new file mode 100644 index 00000000..31f3dc1f --- /dev/null +++ b/examples/data_structures/linear_rmq.rs @@ -0,0 +1,25 @@ +// verification-helper: PROBLEM https://judge.yosupo.jp/problem/staticrmq + +use proconio::input; +use programming_team_code_rust::data_structures::linear_rmq::LinearRMQ; + +fn main() { + input! { + n: usize, + q: usize, + a: [usize; n], + } + + let rmq = LinearRMQ::new(&a, |&x, &y| x.lt(&y)); + for _ in 0..q { + input! { + le: usize, + ri: usize, + } + let idx_min = rmq.query_idx(le..ri); + assert!((le..ri).contains(&idx_min)); + let res = rmq.query(le..ri); + assert_eq!(a[idx_min], *res); + println!("{}", res); + } +} diff --git a/examples/data_structures/range_container_aizu.rs b/examples/data_structures/range_container_aizu.rs new file mode 100644 index 00000000..59fa2794 --- /dev/null +++ b/examples/data_structures/range_container_aizu.rs @@ -0,0 +1,45 @@ +// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/problems/DSL_2_D + +use proconio::input; +use programming_team_code_rust::data_structures::range_container::RangeContainer; + +fn main() { + input! { + n: usize, + q: usize, + } + let mut rc = RangeContainer::default(); + let mut to_value = vec![i32::MAX; 2 * n + 2]; + rc.insert_range(0..(2 * n + 1) as i32); + + for _ in 0..q { + input! { + kind: usize, + } + match kind { + 0 => { + input! { + le: i32, + ri: i32, + x: i32, + } + let le = 2 * le; + let ri = 2 * ri; + let save_range = rc.get_range(ri + 2).unwrap(); + let save_value = to_value[save_range.start as usize]; + rc.remove_range(le - 1..ri + 2); + rc.insert_range(le..ri + 1); + let save_range = rc.get_range(ri + 2).unwrap(); + to_value[save_range.start as usize] = save_value; + to_value[le as usize] = x; + } + _ => { + input! { + index: i32, + } + let range_containing = rc.get_range(2 * index).unwrap(); + println!("{}", to_value[range_containing.start as usize]); + } + } + } +} diff --git a/examples/data_structures/range_container_handmade.rs b/examples/data_structures/range_container_handmade.rs new file mode 100644 index 00000000..e26762a7 --- /dev/null +++ b/examples/data_structures/range_container_handmade.rs @@ -0,0 +1,64 @@ +// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A + +use programming_team_code_rust::data_structures::range_container::RangeContainer; +use rand::{thread_rng, Rng}; +use std::collections::BTreeMap; + +fn main() { + let mut rng = thread_rng(); + for _ in 0..100 { + let max_n = rng.gen_range(1..100); + let mut vis = vec![false; max_n + 1]; + let mut rc = RangeContainer::default(); + for _ in 0..100 { + let mut le = rng.gen_range(0..=max_n); + let mut ri = rng.gen_range(0..=max_n); + if le > ri { + (le, ri) = (ri, le); + } + match rng.gen_range(0..2) { + 0 => { + rc.insert_range(le as i32..ri as i32); + for elem in vis.iter_mut().take(ri).skip(le) { + *elem = true; + } + } + _ => { + rc.remove_range(le as i32..ri as i32); + for elem in vis.iter_mut().take(ri).skip(le) { + *elem = false; + } + } + } + let mut to_end = vec![None; max_n + 1]; + for i in (0..max_n).rev() { + if vis[i] && !vis[i + 1] { + to_end[i] = Some(i + 1); + } else if vis[i] { + to_end[i] = to_end[i + 1]; + } + } + let mut naive_mp = BTreeMap::::new(); + let mut start = None; + for i in 0..max_n + 1 { + if vis[i] { + if start.is_none() { + start = Some(i); + } + assert_eq!( + rc.get_range(i as i32).unwrap(), + start.unwrap() as i32..to_end[i].unwrap() as i32 + ); + } else { + assert_eq!(rc.get_range(i as i32), None); + if let Some(curr_start) = start { + naive_mp.insert(curr_start as i32, i as i32); + } + start = None; + } + } + assert_eq!(rc.mp, naive_mp); + } + } + println!("Hello World"); +} diff --git a/examples/helpers/lis_pop.rs b/examples/helpers/lis_handmade.rs similarity index 89% rename from examples/helpers/lis_pop.rs rename to examples/helpers/lis_handmade.rs index e506c1eb..8815bd89 100644 --- a/examples/helpers/lis_pop.rs +++ b/examples/helpers/lis_handmade.rs @@ -1,6 +1,7 @@ // verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A use programming_team_code_rust::helpers::lis::Lis; +use rand::{thread_rng, Rng}; fn lis_quadratic(a: &[i32]) -> usize { let n = a.len(); @@ -19,13 +20,14 @@ fn lis_quadratic(a: &[i32]) -> usize { } fn main() { + let mut rng = thread_rng(); for _ in 0..100 { let mut lis = Lis::default(); let mut a = Vec::new(); for _ in 0..1000 { - match rand::random::() % 3 { + match rng.gen_range(0..3) { 0 => { - let new_num = rand::random::(); + let new_num = rng.r#gen(); lis.push(new_num); a.push(new_num); } diff --git a/src/data_structures/linear_rmq.rs b/src/data_structures/linear_rmq.rs new file mode 100644 index 00000000..8c8977e1 --- /dev/null +++ b/src/data_structures/linear_rmq.rs @@ -0,0 +1,99 @@ +//! # Linear Range Minimum Query + +/// # Example +/// ``` +/// use programming_team_code_rust::data_structures::linear_rmq::LinearRMQ; +/// +/// let a = [1, 0, 2, 0, 3]; +/// let rmq = LinearRMQ::new(&a, |&x, &y| x.lt(&y)); // lt -> right-most min +/// // le -> left-most min +/// // gt -> right-most max +/// // ge -> left-most max +/// assert_eq!(rmq.query_idx(0..5), 3); +/// assert_eq!(*rmq.query(1..5), 0); +/// ``` +pub struct LinearRMQ { + a: Vec, + cmp: F, + head: Vec, + t: Vec<(usize, usize)>, +} + +impl bool> LinearRMQ { + /// Create a new LinearRMQ instance + /// + /// # Complexity (n = a.len()) + /// - Time: O(n) + /// - Space: O(n) + pub fn new(a: &[T], cmp: F) -> Self { + let mut head = vec![0; a.len() + 1]; + let mut t = vec![(0, 0); a.len()]; + let mut st = vec![usize::MAX]; + for i in 0..=a.len() { + let mut prev = usize::MAX; + while *st.last().unwrap() != usize::MAX + && (i == a.len() || !cmp(&a[*st.last().unwrap()], &a[i])) + { + if prev != usize::MAX { + head[prev] = *st.last().unwrap(); + } + let pw2 = 1 << (st[st.len() - 2].wrapping_add(1) ^ i).ilog2(); + prev = i & 0_usize.wrapping_sub(pw2); + t[*st.last().unwrap()].0 = prev; + st.pop(); + t[(*st.last().unwrap()).wrapping_add(1)].1 |= pw2; + } + if prev != usize::MAX { + head[prev] = i; + } + st.push(i); + } + for i in 1..a.len() { + t[i].1 = + (t[i].1 | t[i - 1].1) & 0_usize.wrapping_sub(t[i].0 & 0_usize.wrapping_sub(t[i].0)); + } + Self { + a: a.to_vec(), + cmp, + head, + t, + } + } + + /// Gets the index of min/max of range + /// + /// # Complexity + /// - Time: O(1) + /// - Space: O(1) + pub fn query_idx(&self, range: std::ops::Range) -> usize { + assert!(!range.is_empty()); + let (mut le, mut ri) = (range.start, range.end - 1); + let j = self.t[le].1 + & self.t[ri].1 + & 0_usize.wrapping_sub(1 << ((self.t[le].0 ^ self.t[ri].0) | 1).ilog2()); + let lift = |u: usize, mut k: usize| -> usize { + if k == 0 { + u + } else { + k = 1 << k.ilog2(); + self.head[self.t[u].0 & 0_usize.wrapping_sub(k) | k] + } + }; + le = lift(le, self.t[le].1 ^ j); + ri = lift(ri, self.t[ri].1 ^ j); + if (self.cmp)(&self.a[le], &self.a[ri]) { + le + } else { + ri + } + } + + /// Gets the min/max of range + /// + /// # Complexity + /// - Time: O(1) + /// - Space: O(1) + pub fn query(&self, range: std::ops::Range) -> &T { + &self.a[self.query_idx(range)] + } +} diff --git a/src/data_structures/mod.rs b/src/data_structures/mod.rs index 23057fc6..6e101f95 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -3,6 +3,8 @@ pub mod binary_trie; pub mod disjoint_rmq; pub mod fenwick; pub mod lazy_seg_tree; +pub mod linear_rmq; +pub mod range_container; pub mod rmq; pub mod seg_tree; pub mod trie; diff --git a/src/data_structures/range_container.rs b/src/data_structures/range_container.rs new file mode 100644 index 00000000..91c96a7e --- /dev/null +++ b/src/data_structures/range_container.rs @@ -0,0 +1,98 @@ +//! # Range Container + +use std::collections::BTreeMap; +use std::ops::Range; + +/// # Example +/// ``` +/// use programming_team_code_rust::data_structures::range_container::RangeContainer; +/// +/// let mut rc = RangeContainer::default(); +/// rc.insert_range(-2..2); +/// rc.insert_range(2..3); +/// assert_eq!(rc.get_range(0), Some(-2..3)); +/// assert_eq!(rc.get_range(3), None); +/// rc.remove_range(-1..1); +/// +/// assert_eq!(rc.get_range(-2), Some(-2..-1)); +/// assert_eq!(rc.get_range(2), Some(1..3)); +/// ``` +#[derive(Default)] +pub struct RangeContainer { + /// an entry le -> ri represents a range [le, ri) + /// invariant: previous_ri < current_le + pub mp: BTreeMap, +} + +impl RangeContainer { + fn remove(&mut self, range: &Range) -> Option { + let mut last_ri = None; + for (le, ri) in self + .mp + .range(range.start..=range.end) + .map(|(&le, &ri)| (le, ri)) + .collect::>() + { + self.mp.remove(&le); + last_ri = Some(ri); + } + last_ri + } + + /// Inserts a range into the container + /// + /// # Complexity + /// - Time: O(log n) ammortized + /// - Space: O(1) ammortized + pub fn insert_range(&mut self, mut range: Range) { + if range.is_empty() { + return; + } + if let Some(last_ri) = self.remove(&range) { + range.end = std::cmp::max(range.end, last_ri); + } + if let Some((_, ri)) = self.mp.range_mut(..range.start).next_back() { + if *ri >= range.start { + *ri = std::cmp::max(*ri, range.end); + return; + } + } + self.mp.insert(range.start, range.end); + } + + /// Removes a range from the container + /// + /// # Complexity + /// - Time: O(log n) ammortized + /// - Space: O(1) ammortized + pub fn remove_range(&mut self, range: Range) { + if range.is_empty() { + return; + } + if let Some(last_ri) = self.remove(&range) { + if range.end < last_ri { + self.mp.insert(range.end, last_ri); + } + } + if let Some((_, ri)) = self.mp.range_mut(..range.start).next_back() { + let ri = std::mem::replace(ri, std::cmp::min(*ri, range.start)); + if range.end < ri { + self.mp.insert(range.end, ri); + } + } + } + + /// Gets range containing idx + /// + /// # Complexity + /// - Time: O(log n) + /// - Space: O(1) + pub fn get_range(&self, idx: T) -> Option> { + if let Some((&le, &ri)) = self.mp.range(..=idx).next_back() { + if idx < ri { + return Some(le..ri); + } + } + None + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}