From 9680b5cecfb554b295144d474de50ca02b45a913 Mon Sep 17 00:00:00 2001 From: Luke Videckis Date: Tue, 16 Jul 2024 22:31:26 -0500 Subject: [PATCH 1/4] Range container (#87) * saving progress * finish insert * rename * finished impl * finished test, need to fix some bugs tho * saving progress * ACs!!! * progress * fix clippy * fix nit * clean * rename * nit * nit * another nit * better var names * more nits * finished docs * rename * raname * wording nit * nit * nits * saving progress * another assert * fix clippy * nit * correct doc * switch stress to handmade * change readme too * switch to templatizing --------- Co-authored-by: Luke Videckis --- Cargo.toml | 14 ++- examples/README.md | 1 + .../helpers/{lis_pop.rs => lis_handmade.rs} | 6 +- examples/helpers/range_container_aizu.rs | 45 +++++++++ examples/helpers/range_container_handmade.rs | 65 +++++++++++++ src/helpers/mod.rs | 1 + src/helpers/range_container.rs | 92 +++++++++++++++++++ 7 files changed, 219 insertions(+), 5 deletions(-) rename examples/helpers/{lis_pop.rs => lis_handmade.rs} (89%) create mode 100644 examples/helpers/range_container_aizu.rs create mode 100644 examples/helpers/range_container_handmade.rs create mode 100644 src/helpers/range_container.rs diff --git a/Cargo.toml b/Cargo.toml index 8093b4ef..f8034772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,8 +201,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/helpers/range_container_aizu.rs" + +[[example]] +name = "range_container_handmade" +path = "examples/helpers/range_container_handmade.rs" [[example]] name = "mono_st" @@ -226,4 +234,4 @@ 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" 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/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/examples/helpers/range_container_aizu.rs b/examples/helpers/range_container_aizu.rs new file mode 100644 index 00000000..b3e43ddf --- /dev/null +++ b/examples/helpers/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::helpers::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/helpers/range_container_handmade.rs b/examples/helpers/range_container_handmade.rs new file mode 100644 index 00000000..db22d32c --- /dev/null +++ b/examples/helpers/range_container_handmade.rs @@ -0,0 +1,65 @@ +// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A + +use programming_team_code_rust::helpers::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); + } + ri += 1; + 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/src/helpers/mod.rs b/src/helpers/mod.rs index 2521979d..06912d56 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,4 +1,5 @@ //! # Helpers pub mod compress; pub mod lis; +pub mod range_container; pub mod unsafe_recursive_closure; diff --git a/src/helpers/range_container.rs b/src/helpers/range_container.rs new file mode 100644 index 00000000..fa3e68ba --- /dev/null +++ b/src/helpers/range_container.rs @@ -0,0 +1,92 @@ +//! # Range Container + +use std::collections::BTreeMap; +use std::ops::Range; + +/// # Example +/// ``` +/// use programming_team_code_rust::helpers::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 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 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 + } +} From f3ca0f5209d260d67ca91ef0bd8dbaed12510c50 Mon Sep 17 00:00:00 2001 From: Luke Videckis Date: Tue, 16 Jul 2024 23:09:49 -0500 Subject: [PATCH 2/4] handle empty ranges (#98) * panic on empty * fix * move to data structures * fix * update test * now ACs --------- Co-authored-by: Luke Videckis --- Cargo.toml | 4 ++-- .../{helpers => data_structures}/range_container_aizu.rs | 2 +- .../range_container_handmade.rs | 7 +++---- src/data_structures/mod.rs | 1 + src/{helpers => data_structures}/range_container.rs | 8 +++++++- src/helpers/mod.rs | 1 - 6 files changed, 14 insertions(+), 9 deletions(-) rename examples/{helpers => data_structures}/range_container_aizu.rs (94%) rename examples/{helpers => data_structures}/range_container_handmade.rs (91%) rename src/{helpers => data_structures}/range_container.rs (92%) diff --git a/Cargo.toml b/Cargo.toml index f8034772..3a8b26c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,11 +206,11 @@ path = "examples/helpers/lis_handmade.rs" [[example]] name = "range_container_aizu" -path = "examples/helpers/range_container_aizu.rs" +path = "examples/data_structures/range_container_aizu.rs" [[example]] name = "range_container_handmade" -path = "examples/helpers/range_container_handmade.rs" +path = "examples/data_structures/range_container_handmade.rs" [[example]] name = "mono_st" diff --git a/examples/helpers/range_container_aizu.rs b/examples/data_structures/range_container_aizu.rs similarity index 94% rename from examples/helpers/range_container_aizu.rs rename to examples/data_structures/range_container_aizu.rs index b3e43ddf..59fa2794 100644 --- a/examples/helpers/range_container_aizu.rs +++ b/examples/data_structures/range_container_aizu.rs @@ -1,7 +1,7 @@ // verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/problems/DSL_2_D use proconio::input; -use programming_team_code_rust::helpers::range_container::RangeContainer; +use programming_team_code_rust::data_structures::range_container::RangeContainer; fn main() { input! { diff --git a/examples/helpers/range_container_handmade.rs b/examples/data_structures/range_container_handmade.rs similarity index 91% rename from examples/helpers/range_container_handmade.rs rename to examples/data_structures/range_container_handmade.rs index db22d32c..e26762a7 100644 --- a/examples/helpers/range_container_handmade.rs +++ b/examples/data_structures/range_container_handmade.rs @@ -1,6 +1,6 @@ // verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A -use programming_team_code_rust::helpers::range_container::RangeContainer; +use programming_team_code_rust::data_structures::range_container::RangeContainer; use rand::{thread_rng, Rng}; use std::collections::BTreeMap; @@ -11,12 +11,11 @@ fn main() { 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); + 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); } - ri += 1; match rng.gen_range(0..2) { 0 => { rc.insert_range(le as i32..ri as i32); diff --git a/src/data_structures/mod.rs b/src/data_structures/mod.rs index 23057fc6..3ea3e01c 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -3,6 +3,7 @@ pub mod binary_trie; pub mod disjoint_rmq; pub mod fenwick; pub mod lazy_seg_tree; +pub mod range_container; pub mod rmq; pub mod seg_tree; pub mod trie; diff --git a/src/helpers/range_container.rs b/src/data_structures/range_container.rs similarity index 92% rename from src/helpers/range_container.rs rename to src/data_structures/range_container.rs index fa3e68ba..91c96a7e 100644 --- a/src/helpers/range_container.rs +++ b/src/data_structures/range_container.rs @@ -5,7 +5,7 @@ use std::ops::Range; /// # Example /// ``` -/// use programming_team_code_rust::helpers::range_container::RangeContainer; +/// use programming_team_code_rust::data_structures::range_container::RangeContainer; /// /// let mut rc = RangeContainer::default(); /// rc.insert_range(-2..2); @@ -45,6 +45,9 @@ impl RangeContainer { /// - 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); } @@ -63,6 +66,9 @@ impl RangeContainer { /// - 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); diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 06912d56..2521979d 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,5 +1,4 @@ //! # Helpers pub mod compress; pub mod lis; -pub mod range_container; pub mod unsafe_recursive_closure; From 6d25c46e41fe2814021c7a63a31ccaab9e0405fc Mon Sep 17 00:00:00 2001 From: Luke Videckis Date: Mon, 22 Jul 2024 11:09:18 -0500 Subject: [PATCH 3/4] unneeded (#99) Co-authored-by: Luke Videckis --- src/main.rs | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/main.rs 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!"); -} From 13ba2209b919f282da18c25f3e5e6e0b4746a994 Mon Sep 17 00:00:00 2001 From: Luke Videckis Date: Mon, 22 Jul 2024 14:01:59 -0500 Subject: [PATCH 4/4] linear rmq (#100) * linear rmq * add docs * better golf --------- Co-authored-by: Luke Videckis --- Cargo.toml | 4 ++ examples/data_structures/linear_rmq.rs | 25 +++++++ src/data_structures/linear_rmq.rs | 99 ++++++++++++++++++++++++++ src/data_structures/mod.rs | 1 + 4 files changed, 129 insertions(+) create mode 100644 examples/data_structures/linear_rmq.rs create mode 100644 src/data_structures/linear_rmq.rs diff --git a/Cargo.toml b/Cargo.toml index 3a8b26c7..2785281f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -235,3 +235,7 @@ 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" + +[[example]] +name = "linear_rmq" +path = "examples/data_structures/linear_rmq.rs" 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/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 3ea3e01c..6e101f95 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -3,6 +3,7 @@ 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;