From 188350104f79a8227b1702b11175677763b4e94b Mon Sep 17 00:00:00 2001 From: DeaL <57758351+dailing57@users.noreply.github.com> Date: Thu, 19 May 2022 14:54:15 +0800 Subject: [PATCH 1/5] doc(comments):hashmap (#23) --- src/caching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/caching.rs b/src/caching.rs index 668ef75..ed22656 100644 --- a/src/caching.rs +++ b/src/caching.rs @@ -1,5 +1,5 @@ //! Basic Cacher struct which stores a closure and a hashmap. -//! The hasmap stores key value pairs representing previous +//! The hashmap stores key value pairs representing previous //! function calls. //! //! When the Cacher function is run, it first does a lookup From 6198cf16f667859ca60babb4b2264b9b9d039ade Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 22 May 2022 15:24:21 -0700 Subject: [PATCH 2/5] Rust 2021 edition --- .travis.yml | 3 +-- Cargo.toml | 4 ++-- src/graph/util.rs | 2 +- src/scanner.rs | 6 ++---- src/string_proc.rs | 5 +++-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b620ec..a99bd14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.51.0 # Version currently supported by Codeforces + #- 1.58.0 # Version currently supported by Codeforces - stable - beta - nightly @@ -12,5 +12,4 @@ script: matrix: allow_failures: - rust: nightly - - rust: beta # clippy currently has a bug on beta fast_finish: true diff --git a/Cargo.toml b/Cargo.toml index 1b965ce..ef526b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "contest-algorithms" -version = "0.3.0" +version = "0.3.1-alpha.0" authors = ["Aram Ebtekar"] -edition = "2018" +edition = "2021" description = "Common algorithms and data structures for programming contests" repository = "https://github.com/EbTech/rust-algorithms" diff --git a/src/graph/util.rs b/src/graph/util.rs index 9868721..b7a3817 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -91,7 +91,7 @@ impl<'a> Iterator for DfsIterator<'a> { fn next(&mut self) -> Option { loop { let &u = self.stack.last()?; - while let Some((e, v)) = self.adj_iters[u].next() { + for (e, v) in self.adj_iters[u].by_ref() { if !self.visited[v] { self.visited[v] = true; self.stack.push(v); diff --git a/src/scanner.rs b/src/scanner.rs index a57b408..83fb816 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -35,7 +35,6 @@ impl Scanner { } /// Same API as Scanner but nearly twice as fast, using horribly unsafe dark arts -/// **REQUIRES** Rust 1.34 or higher pub struct UnsafeScanner { reader: R, buf_str: Vec, @@ -118,9 +117,8 @@ mod test { #[test] fn test_compile_stdio() { - let (stdin, stdout) = (io::stdin(), io::stdout()); - let mut scan = Scanner::new(stdin.lock()); - let mut out = io::BufWriter::new(stdout.lock()); + let mut scan = Scanner::new(io::stdin().lock()); + let mut out = io::BufWriter::new(io::stdout().lock()); if false { solve(&mut scan, &mut out); diff --git a/src/string_proc.rs b/src/string_proc.rs index 287ed9a..888bf67 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -142,6 +142,7 @@ impl MultiMatcher { /// If there are duplicate patterns, all but one copy will be ignored. pub fn new(patterns: impl IntoIterator>) -> Self { let mut trie = Trie::default(); + #[allow(clippy::needless_collect)] // It's not needless: it affects trie.links.len() let pat_nodes: Vec = patterns.into_iter().map(|pat| trie.insert(pat)).collect(); let mut pat_id = vec![None; trie.links.len()]; @@ -155,7 +156,7 @@ impl MultiMatcher { while let Some(node) = q.pop_front() { for (ch, &child) in &trie.links[node] { - let nx = Self::next(&trie, &fail, fail[node], &ch); + let nx = Self::next(&trie, &fail, fail[node], ch); fail[child] = nx; fast[child] = if pat_id[nx].is_some() { nx } else { fast[nx] }; q.push_back(child); @@ -246,7 +247,7 @@ impl SuffixArray { let mut cur_rank = prev_rank.clone(); let pos = (n - skip..n).chain(sfx.into_iter().filter_map(|p| p.checked_sub(skip))); - sfx = Self::counting_sort(pos, &prev_rank, max(n, 256)); + sfx = Self::counting_sort(pos, prev_rank, max(n, 256)); let mut prev = sfx[0]; cur_rank[prev] = 0; From b2968ff62ac792fc920a048465db6c59797f8fbe Mon Sep 17 00:00:00 2001 From: Naman Garg <66401604+namanlp@users.noreply.github.com> Date: Thu, 9 Mar 2023 03:34:40 +0000 Subject: [PATCH 3/5] Add Codechef to supported websites (#25) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cae59f9..be5b5d9 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ To my delight, I found that Rust eliminates entire classes of bugs, while reduci Some contest sites and online judges that support Rust: - [Codeforces](https://codeforces.com) +- [CodeChef](https://www.codechef.com) - [AtCoder](https://atcoder.jp) - [Kattis](https://open.kattis.com/help/rust) - [SPOJ](https://www.spoj.com/) From 6c5d5444e7ea952166b4aa15f293ace36106123f Mon Sep 17 00:00:00 2001 From: Fr0benius Date: Sun, 19 Mar 2023 16:25:37 -0700 Subject: [PATCH 4/5] Li Chao tree (#16) A second data structure for maintaining the maximum of a set of lines ("convex hull trick"), implemented by @Fr0benius. --- src/li_chao.rs | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 src/li_chao.rs diff --git a/src/li_chao.rs b/src/li_chao.rs new file mode 100644 index 0000000..f69a0ae --- /dev/null +++ b/src/li_chao.rs @@ -0,0 +1,110 @@ +/// A structure for answering maximum queries on a set of linear functions. Supports two +/// operations: inserting a linear function and querying for maximum at a given point. +/// The queries can be done in any order, and we can do all the calculations using integers. +/// https://cp-algorithms.com/geometry/convex_hull_trick.html#li-chao-tree +/// Compared to the code in the above link, this implementation further improves the algorithm by +/// reducing the number of nodes to (right - left). This is done by removing the midpoint of a +/// segment from both children. Even better, this allows the index of a node to just be the +/// midpoint of the interval! + +/// Just like normal segment trees, this could be modified to a dynamic tree when the range is +/// huge, or if the queries are known in advance the x-coordinates can be compressed. +/// (it can also be made persistent!). + +pub struct LiChaoTree { + left: i64, + right: i64, + lines: Vec<(i64, i64)>, +} + +impl LiChaoTree { + /// Creates a new tree, built to handle queries on the interval [left, right). + pub fn new(left: i64, right: i64) -> Self { + Self { + left, + right, + lines: vec![(0, std::i64::MIN); (right - left) as usize], + } + } + + /// Every node in the tree has the property that the line that maximizes its midpoint is found + /// either in the node or one of its ancestors. When we visit a node, we compute the winner at + /// the midpoint of the node. The winner is stored in the node. The loser can still possibly + /// beat the winner on some segment, either to the left or to the right of the current + /// midpoint, so we propagate it to that segment. This sequence ensures that the invariant is + /// kept. + fn max_with_impl(&mut self, mut m: i64, mut b: i64, l: i64, r: i64) { + if r <= l { + return; + } + let ix = ((r - self.left + l - self.left) / 2) as usize; + let mid = self.left + (ix as i64); + let (ref mut m_ix, ref mut b_ix) = self.lines[ix]; + if m * mid + b > *m_ix * mid + *b_ix { + std::mem::swap(&mut m, m_ix); + std::mem::swap(&mut b, b_ix); + } + if m < *m_ix { + self.max_with_impl(m, b, l, mid); + } else if m > *m_ix { + self.max_with_impl(m, b, mid + 1, r); + } + } + + /// Adds the line with slope m and intercept b. O(log N) complexity. + pub fn max_with(&mut self, m: i64, b: i64) { + self.max_with_impl(m, b, self.left, self.right); + } + + /// Because of the invariant established by add_line, we know that the best line for a given + /// point is stored in one of the ancestors of its node. So we accumulate the maximum answer as + /// we go back up the tree. + fn evaluate_impl(&self, x: i64, l: i64, r: i64) -> i64 { + if r == l { + return i64::MIN; + } + let ix = ((r - self.left + l - self.left) / 2) as usize; + let mid = ix as i64 + self.left; + let y = self.lines[ix].0 * x + self.lines[ix].1; + if x == mid { + y + } else if x < mid { + self.evaluate_impl(x, l, mid).max(y) + } else { + self.evaluate_impl(x, mid + 1, r).max(y) + } + } + + /// Finds the maximum mx+b among all lines in the structure. O(log N) complexity. + pub fn evaluate(&self, x: i64) -> i64 { + self.evaluate_impl(x, self.left, self.right) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_li_chao_tree() { + let lines = [(0, -3), (-1, 0), (1, -8), (-2, 1), (1, -4)]; + let xs = [0, 1, 2, 3, 4, 5]; + // results[i] consists of the expected y-coordinates after processing + // the first i+1 lines. + let results = [ + [-3, -3, -3, -3, -3, -3], + [0, -1, -2, -3, -3, -3], + [0, -1, -2, -3, -3, -3], + [1, -1, -2, -3, -3, -3], + [1, -1, -2, -1, 0, 1], + ]; + let mut li_chao = LiChaoTree::new(0, 6); + + assert_eq!(li_chao.evaluate(0), std::i64::MIN); + for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { + li_chao.max_with(slope, intercept); + let ys: Vec = xs.iter().map(|&x| li_chao.evaluate(x)).collect(); + assert_eq!(expected, &ys[..]); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6a96038..ba482d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod caching; pub mod graph; +pub mod li_chao; pub mod math; pub mod order; pub mod range_query; From 2073670c540935960849f365e1895f558fa4319d Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Thu, 27 Feb 2025 12:20:03 -0800 Subject: [PATCH 5/5] Rust 2024 edition --- Cargo.toml | 4 ++-- README.md | 5 +---- src/graph/flow.rs | 2 +- src/graph/mod.rs | 2 +- src/graph/util.rs | 10 +++++----- src/li_chao.rs | 9 ++++----- src/math/mod.rs | 6 +----- src/range_query/dynamic_arq.rs | 4 ++-- src/range_query/specs.rs | 2 +- src/range_query/static_arq.rs | 6 +++--- src/string_proc.rs | 2 +- tests/codeforces343d.rs | 2 +- 12 files changed, 23 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef526b1..ae11d8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "contest-algorithms" -version = "0.3.1-alpha.0" +version = "0.3.1-alpha.1" authors = ["Aram Ebtekar"] -edition = "2021" +edition = "2024" description = "Common algorithms and data structures for programming contests" repository = "https://github.com/EbTech/rust-algorithms" diff --git a/README.md b/README.md index be5b5d9..2add26f 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,7 @@ Some contest sites and online judges that support Rust: - [HackerRank](https://www.hackerrank.com/contests) - [Timus](http://acm.timus.ru/help.aspx?topic=rust) -The following support pre-2018 versions of Rust: -- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - -For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). +For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038) (requires login). ## Programming Language Advocacy diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 0b6e684..41af65a 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -143,7 +143,7 @@ impl FlowGraph { let (mut min_cost, mut max_flow) = (0, 0); loop { let par = self.mcf_search(s, &flow, &mut pot); - if par[t] == None { + if par[t].is_none() { break; } let (dc, df) = self.mcf_augment(t, &par, &mut flow); diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 41d8e30..f5a4c9e 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -111,7 +111,7 @@ pub struct AdjListIterator<'a> { next_e: Option, } -impl<'a> Iterator for AdjListIterator<'a> { +impl Iterator for AdjListIterator<'_> { type Item = (usize, usize); /// Produces an outgoing edge and vertex. diff --git a/src/graph/util.rs b/src/graph/util.rs index b7a3817..bf38f19 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -12,16 +12,16 @@ impl Graph { .map(|u| self.adj_list(u)) .collect::>(); let mut edges = Vec::with_capacity(self.num_e()); - self.euler_recurse(u, &mut adj_iters, &mut edges); + Self::euler_recurse(u, &mut adj_iters, &mut edges); edges.reverse(); edges } // Helper function used by euler_path. Note that we can't use a for-loop // that would consume the adjacency list as recursive calls may need it. - fn euler_recurse(&self, u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { + fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { while let Some((e, v)) = adj[u].next() { - self.euler_recurse(v, adj, edges); + Self::euler_recurse(v, adj, edges); edges.push(e); } } @@ -42,7 +42,7 @@ impl Graph { // Single-source shortest paths on a directed graph with non-negative weights pub fn dijkstra(&self, weights: &[u64], u: usize) -> Vec { assert_eq!(self.num_e(), weights.len()); - let mut dist = vec![u64::max_value(); weights.len()]; + let mut dist = vec![u64::MAX; weights.len()]; let mut heap = std::collections::BinaryHeap::new(); dist[u] = 0; @@ -82,7 +82,7 @@ pub struct DfsIterator<'a> { adj_iters: Vec>, } -impl<'a> Iterator for DfsIterator<'a> { +impl Iterator for DfsIterator<'_> { type Item = (usize, usize); /// Returns next edge and vertex in the depth-first traversal diff --git a/src/li_chao.rs b/src/li_chao.rs index f69a0ae..39cbb74 100644 --- a/src/li_chao.rs +++ b/src/li_chao.rs @@ -1,16 +1,15 @@ /// A structure for answering maximum queries on a set of linear functions. Supports two -/// operations: inserting a linear function and querying for maximum at a given point. +/// operations: inserting a linear function and querying for maximum at a given point. /// The queries can be done in any order, and we can do all the calculations using integers. /// https://cp-algorithms.com/geometry/convex_hull_trick.html#li-chao-tree /// Compared to the code in the above link, this implementation further improves the algorithm by /// reducing the number of nodes to (right - left). This is done by removing the midpoint of a /// segment from both children. Even better, this allows the index of a node to just be the /// midpoint of the interval! - +/// /// Just like normal segment trees, this could be modified to a dynamic tree when the range is /// huge, or if the queries are known in advance the x-coordinates can be compressed. /// (it can also be made persistent!). - pub struct LiChaoTree { left: i64, right: i64, @@ -23,7 +22,7 @@ impl LiChaoTree { Self { left, right, - lines: vec![(0, std::i64::MIN); (right - left) as usize], + lines: vec![(0, i64::MIN); (right - left) as usize], } } @@ -100,7 +99,7 @@ mod test { ]; let mut li_chao = LiChaoTree::new(0, 6); - assert_eq!(li_chao.evaluate(0), std::i64::MIN); + assert_eq!(li_chao.evaluate(0), i64::MIN); for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { li_chao.max_with(slope, intercept); let ys: Vec = xs.iter().map(|&x| li_chao.evaluate(x)).collect(); diff --git a/src/math/mod.rs b/src/math/mod.rs index 42127cc..c27e7af 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -31,11 +31,7 @@ pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> { // TODO: deduplicate modular arithmetic code with num::Field fn pos_mod(n: i64, m: i64) -> i64 { - if n < 0 { - n + m - } else { - n - } + if n < 0 { n + m } else { n } } fn mod_mul(a: i64, b: i64, m: i64) -> i64 { pos_mod((a as i128 * b as i128 % m as i128) as i64, m) diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index f33eaaf..6450468 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -24,7 +24,7 @@ impl Default for DynamicArqNode { Self { val: T::identity(), app: None, - down: (usize::max_value(), usize::max_value()), + down: (usize::MAX, usize::MAX), } } } @@ -97,7 +97,7 @@ impl DynamicArq { } pub fn push(&mut self, (p, s): ArqView) -> (ArqView, ArqView) { - if self.nodes[p].down.0 == usize::max_value() { + if self.nodes[p].down.0 == usize::MAX { self.nodes.push(DynamicArqNode::default()); self.nodes.push(DynamicArqNode::default()); self.nodes[p].down = (self.nodes.len() - 2, self.nodes.len() - 1) diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs index 1340a4c..9a70411 100644 --- a/src/range_query/specs.rs +++ b/src/range_query/specs.rs @@ -50,7 +50,7 @@ impl ArqSpec for AssignMin { a.min(b) } fn identity() -> Self::S { - i64::max_value() + i64::MAX } fn compose(&f: &Self::F, _: &Self::F) -> Self::F { f diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index 14a9331..461986d 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -50,14 +50,14 @@ impl StaticArq { fn push(&mut self, p: usize) { if let Some(ref f) = self.app[p].take() { - let s = ((self.app.len() + p - 1) / p / 2).next_power_of_two() as i64; + let s = (self.app.len().div_ceil(p) / 2).next_power_of_two() as i64; self.apply(p << 1, f, s); - self.apply(p << 1 | 1, f, s); + self.apply((p << 1) | 1, f, s); } } fn pull(&mut self, p: usize) { - self.val[p] = T::op(&self.val[p << 1], &self.val[p << 1 | 1]); + self.val[p] = T::op(&self.val[p << 1], &self.val[(p << 1) | 1]); } fn push_to(&mut self, p: usize) { diff --git a/src/string_proc.rs b/src/string_proc.rs index 888bf67..4716f68 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -1,6 +1,6 @@ //! String processing algorithms. use std::cmp::{max, min}; -use std::collections::{hash_map::Entry, HashMap, VecDeque}; +use std::collections::{HashMap, VecDeque, hash_map::Entry}; /// Prefix trie, easily augmentable by adding more fields and/or methods pub struct Trie { diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 94b911a..ec0f447 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -4,7 +4,7 @@ //! Also, use the commented code in main() to employ standard I/O. extern crate contest_algorithms; use contest_algorithms::graph::Graph; -use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; +use contest_algorithms::range_query::{StaticArq, specs::AssignSum}; use contest_algorithms::scanner::Scanner; use std::io;