From 3a51138fe5e054ba19b0670dbc8db0571071c390 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 19 Jun 2017 23:43:12 -0700 Subject: [PATCH 01/71] Create .travis.yml --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8c91a74 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: rust +rust: + - stable + - beta + - nightly +matrix: + allow_failures: + - rust: nightly From efc185dbf77e705df6845176923a5e9a80437335 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 20 Jun 2017 00:00:45 -0700 Subject: [PATCH 02/71] Added Travis CI badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 84140a0..e4b1064 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Algorithm Cookbook in Rust +[![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) + A collection of classic data structures and algorithms, emphasizing beauty and clarity over full generality. As such, this should not be viewed as a blackbox *library*, but as a whitebox *cookbook* demonstrating the translation of abstract concepts into executable code. I hope it will be useful to students and educators, as well as competition programmers. This repository is distributed under the [MIT License](LICENSE). The license text need not be included in contest submissions, though I would appreciate linking back to this repo for others to find. Enjoy! From 72524d7c0463f95411e891aaa53571ef3f45e614 Mon Sep 17 00:00:00 2001 From: EbTech Date: Wed, 21 Jun 2017 01:06:32 -0700 Subject: [PATCH 03/71] Preliminary implementation of a generic ArqTree --- src/arq_tree.rs | 104 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 3fbeb2f..4835f69 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -7,46 +7,47 @@ /// /// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for a homomorphism f /// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r -/// -/// To customize, simply change the commented lines. -/// In this example, we chose to support range sum queries and range constant -/// assignments. Since constant assignment f_c(a) = c is not a homomorphism over -/// integers, we have to augment the monoid type, using the 2D vector (a_i, 1) -/// instead of a_i. You may check that f_c((a, s)) = (c*s, s) is a homomorphism. -pub struct ArqTree { - d: Vec>, - t: Vec, - s: Vec, +pub struct ArqTree { + d: Vec>, + t: Vec, } -impl ArqTree { - /// Initializes a sequence of identity elements. - pub fn new(size: usize) -> Self { - let mut s = vec![1; 2 * size]; +impl ArqTree +where + T::H: Clone, +{ + /// Initializes a static balanced tree on top of the given sequence. + pub fn new(mut init: Vec) -> Self { + let size = init.len(); + let mut t = (0..size).map(|_| T::identity()).collect::>(); + t.append(&mut init); for i in (0..size).rev() { - s[i] = s[i << 1] + s[i << 1 | 1]; + t[i] = T::op(&t[i << 1], &t[i << 1 | 1]); } Self { - d: vec![None; size], - t: vec![0; 2 * size], // monoid identity - s: s, + d: (0..size).map(|_| None).collect(), + t: t, } } - fn apply(&mut self, p: usize, f: i64) { - self.t[p] = f * self.s[p]; // hom application + fn apply(&mut self, p: usize, f: &T::H) { + self.t[p] = T::apply(f, &self.t[p]); // hom application if p < self.d.len() { - self.d[p] = Some(f); // hom composition + let h = if let Some(ref g) = self.d[p] { + T::compose(f, g) + } else { + f.clone() + }; + self.d[p] = Some(h); } } fn push(&mut self, p: usize) { for s in (1..32).rev() { let i = p >> s; - if let Some(f) = self.d[i] { + if let Some(ref f) = self.d[i].take() { self.apply(i << 1, f); self.apply(i << 1 | 1, f); - self.d[i] = None; } } } @@ -54,14 +55,14 @@ impl ArqTree { fn pull(&mut self, mut p: usize) { while p > 1 { p >>= 1; - if self.d[p] == None { - self.t[p] = self.t[p << 1] + self.t[p << 1 | 1]; // monoid op + if self.d[p].is_none() { + self.t[p] = T::op(&self.t[p << 1], &self.t[p << 1 | 1]); // monoid op } } } /// Performs the homomorphism f on all entries from l to r, inclusive. - pub fn modify(&mut self, mut l: usize, mut r: usize, f: i64) { + pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::H) { l += self.d.len(); r += self.d.len(); let (l0, r0) = (l, r); @@ -84,19 +85,19 @@ impl ArqTree { } /// Returns the aggregate range query on all entries from l to r, inclusive. - pub fn query(&mut self, mut l: usize, mut r: usize) -> i64 { + pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { l += self.d.len(); r += self.d.len(); self.push(l); self.push(r); - let mut res = 0; // monoid identity + let mut res = T::identity(); // monoid identity while l <= r { if l & 1 == 1 { - res = res + self.t[l]; // monoid op + res = T::op(&res, &self.t[l]); // monoid op l += 1; } if r & 1 == 0 { - res = self.t[r] + res; // monoid op + res = T::op(&self.t[r], &res); // monoid op r -= 1; } l >>= 1; @@ -106,17 +107,52 @@ impl ArqTree { } } +pub trait ArqSpec { + type H; + type M; + fn compose(f: &Self::H, g: &Self::H) -> Self::H; + fn apply(f: &Self::H, a: &Self::M) -> Self::M; + fn op(a: &Self::M, b: &Self::M) -> Self::M; + fn identity() -> Self::M; +} + +/// In this example, we chose to support range sum queries and range constant +/// assignments. Since constant assignment f_c(a) = c is not a homomorphism over +/// integers, we have to augment the monoid type, using the 2D vector (a_i, 1) +/// instead of a_i. You may check that f_c((a, s)) = (c*s, s) is a homomorphism. +pub struct AssignAdd; + +impl ArqSpec for AssignAdd { + type H = i64; + type M = (i64, i64); + fn compose(f: &Self::H, _: &Self::H) -> Self::H { + *f + } + fn apply(f: &Self::H, a: &Self::M) -> Self::M { + (f * a.1, a.1) + } + fn op(a: &Self::M, b: &Self::M) -> Self::M { + (a.0 + b.0, a.1 + b.1) + } + fn identity() -> Self::M { + (0, 0) + } +} + + #[cfg(test)] mod test { use super::*; #[test] fn test_arq_tree() { - let mut arq = ArqTree::new(10); + let mut arq = ArqTree::::new(vec![(0, 1); 10]); + + assert_eq!(arq.query(0, 9), (0, 10)); - arq.modify(1, 3, 10); - arq.modify(3, 5, 1); + arq.modify(1, 3, &10); + arq.modify(3, 5, &1); - assert_eq!(arq.query(0, 9), 23); + assert_eq!(arq.query(0, 9), (23, 10)); } } From 78a5cf980a52c488d8d01f8645fa5b67b5025515 Mon Sep 17 00:00:00 2001 From: EbTech Date: Sat, 24 Jun 2017 17:20:43 -0700 Subject: [PATCH 04/71] ArqTree no longer assumes commutativity --- src/arq_tree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 4835f69..0f63d42 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -31,7 +31,7 @@ where } fn apply(&mut self, p: usize, f: &T::H) { - self.t[p] = T::apply(f, &self.t[p]); // hom application + self.t[p] = T::apply(f, &self.t[p]); if p < self.d.len() { let h = if let Some(ref g) = self.d[p] { T::compose(f, g) @@ -56,7 +56,7 @@ where while p > 1 { p >>= 1; if self.d[p].is_none() { - self.t[p] = T::op(&self.t[p << 1], &self.t[p << 1 | 1]); // monoid op + self.t[p] = T::op(&self.t[p << 1], &self.t[p << 1 | 1]); } } } @@ -90,20 +90,20 @@ where r += self.d.len(); self.push(l); self.push(r); - let mut res = T::identity(); // monoid identity + let (mut l_agg, mut r_agg) = (T::identity(), T::identity()); while l <= r { if l & 1 == 1 { - res = T::op(&res, &self.t[l]); // monoid op + l_agg = T::op(&l_agg, &self.t[l]); l += 1; } if r & 1 == 0 { - res = T::op(&self.t[r], &res); // monoid op + r_agg = T::op(&self.t[r], &r_agg); r -= 1; } l >>= 1; r >>= 1; } - res + T::op(&l_agg, &r_agg) } } From afcd381efec907e1e27dd83245a585a1eb2505bf Mon Sep 17 00:00:00 2001 From: EbTech Date: Tue, 4 Jul 2017 17:24:32 -0700 Subject: [PATCH 05/71] Added solution to Codeforces 343D (Water Tree), a fairly challenging problem with large input size --- src/arq_tree.rs | 54 ++++++++++-------- src/graph/flow.rs | 8 +-- src/graph/mod.rs | 19 ++++--- src/scanner.rs | 2 +- tests/codeforces343d.rs | 115 +++++++++++++++++++++++++++++++++++++++ tests/example_problem.rs | 28 ---------- 6 files changed, 161 insertions(+), 65 deletions(-) create mode 100644 tests/codeforces343d.rs delete mode 100644 tests/example_problem.rs diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 0f63d42..aa6a7ec 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -2,19 +2,19 @@ //! (http://codeforces.com/blog/entry/18051). /// Colloquially known as a "segtree" in the sport programming literature, it -/// represents a sequence of elements a_i (0 <= i < size) from a monoid (M, +) on -/// which we want to support fast range operations: +/// represents a sequence of elements a_i (0 <= i < size) from a monoid (M, +) +/// on which we want to support fast range operations: /// -/// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for a homomorphism f +/// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f /// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r pub struct ArqTree { - d: Vec>, + d: Vec>, t: Vec, } impl ArqTree where - T::H: Clone, + T::F: Clone, { /// Initializes a static balanced tree on top of the given sequence. pub fn new(mut init: Vec) -> Self { @@ -25,18 +25,17 @@ where t[i] = T::op(&t[i << 1], &t[i << 1 | 1]); } Self { - d: (0..size).map(|_| None).collect(), + d: vec![None; size], t: t, } } - fn apply(&mut self, p: usize, f: &T::H) { + fn apply(&mut self, p: usize, f: &T::F) { self.t[p] = T::apply(f, &self.t[p]); if p < self.d.len() { - let h = if let Some(ref g) = self.d[p] { - T::compose(f, g) - } else { - f.clone() + let h = match self.d[p] { + Some(ref g) => T::compose(f, g), + None => f.clone(), }; self.d[p] = Some(h); } @@ -61,8 +60,8 @@ where } } - /// Performs the homomorphism f on all entries from l to r, inclusive. - pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::H) { + /// Performs the endomorphism f on all entries from l to r, inclusive. + pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { l += self.d.len(); r += self.d.len(); let (l0, r0) = (l, r); @@ -108,27 +107,36 @@ where } pub trait ArqSpec { - type H; + type F; type M; - fn compose(f: &Self::H, g: &Self::H) -> Self::H; - fn apply(f: &Self::H, a: &Self::M) -> Self::M; + /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) + fn compose(f: &Self::F, g: &Self::F) -> Self::F; + /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) + fn apply(f: &Self::F, a: &Self::M) -> Self::M; + /// Require for alla,b,c: op(a, op(b, c)) = op(op(a, b), c) fn op(a: &Self::M, b: &Self::M) -> Self::M; + /// Require all a: op(a, identity()) = op(identity(), a) = a fn identity() -> Self::M; } -/// In this example, we chose to support range sum queries and range constant -/// assignments. Since constant assignment f_c(a) = c is not a homomorphism over -/// integers, we have to augment the monoid type, using the 2D vector (a_i, 1) -/// instead of a_i. You may check that f_c((a, s)) = (c*s, s) is a homomorphism. +/// In this example, we want to support range sum queries and range constant +/// assignments. Note that constant assignment f_c(a) = c is not a endomorphism +/// on integers because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive +/// terms, the problem is that the internal nodes of the tree should really be +/// set to a multiple of c, corresponding to the subtree size. So let's augment +/// the monoid type with size information, using the 2D vector (a_i,1) instead +/// of a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism: +/// f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) = (c*s,s)+(c*t,t) = +/// f_c((a,s)) + f_c((b,t)). pub struct AssignAdd; impl ArqSpec for AssignAdd { - type H = i64; + type F = i64; type M = (i64, i64); - fn compose(f: &Self::H, _: &Self::H) -> Self::H { + fn compose(f: &Self::F, _: &Self::F) -> Self::F { *f } - fn apply(f: &Self::H, a: &Self::M) -> Self::M { + fn apply(f: &Self::F, a: &Self::M) -> Self::M { (f * a.1, a.1) } fn op(a: &Self::M, b: &Self::M) -> Self::M { diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 0f17bf6..9e02c69 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -12,11 +12,11 @@ pub struct FlowGraph { impl FlowGraph { /// Initializes an flow network with vmax vertices and no edges. - pub fn new(vmax: usize, emax: usize) -> Self { + pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - graph: Graph::new(vmax, 2 * emax), - cap: Vec::with_capacity(2 * emax), - cost: Vec::with_capacity(2 * emax), + graph: Graph::new(vmax, 2 * emax_hint), + cap: Vec::with_capacity(2 * emax_hint), + cost: Vec::with_capacity(2 * emax_hint), } } diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 6ed10ef..b9782e1 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -33,21 +33,22 @@ impl DisjointSets { } } -/// A compact graph representation. +/// A compact graph representation. Edges are numbered in order of insertion. pub struct Graph { - pub first: Vec>, - pub next: Vec>, - pub endp: Vec, + first: Vec>, + next: Vec>, + endp: Vec, } impl Graph { - /// Initializes a graph with vmax vertices and no edges. For best efficiency, - /// emax should be a tight upper bound on the number of edges to insert. - pub fn new(vmax: usize, emax: usize) -> Self { + /// Initializes a graph with vmax vertices and no edges. To reduce + /// unnecessary allocations, emax_hint should be close to the number of + /// edges that will be inserted. + pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { first: vec![None; vmax], - next: Vec::with_capacity(emax), - endp: Vec::with_capacity(emax), + next: Vec::with_capacity(emax_hint), + endp: Vec::with_capacity(emax_hint), } } diff --git a/src/scanner.rs b/src/scanner.rs index cb49dfc..ab384be 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -56,7 +56,7 @@ mod test { } #[test] - #[should_panic] + #[should_panic(expected = "File not found")] fn test_file() { let file = ::std::fs::File::open("asdf.txt").expect("File not found"); let mut scan = Scanner::new(io::BufReader::new(file)); diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs new file mode 100644 index 0000000..7cabd81 --- /dev/null +++ b/tests/codeforces343d.rs @@ -0,0 +1,115 @@ +//! Solves [Water Tree](http://codeforces.com/contest/343/problem/D). +//! To make a self-contained file for contest submission, dump each desired +//! module's contents directly here instead of the use statements. +//! Also, replace io::Cursor with io::stdin as shown in scanner.rs. +extern crate algorithms; +use std::fmt::Write as FmtWrite; +use std::io::Cursor; +use algorithms::arq_tree::{ArqTree, AssignAdd}; +use algorithms::graph::Graph; +use algorithms::scanner::Scanner; + +const SAMPLE_INPUT: &str = "\ +5 +1 2 +5 1 +2 3 +4 2 +12 +1 1 +2 3 +3 1 +3 2 +3 3 +3 4 +1 2 +2 4 +3 1 +3 3 +3 4 +3 5 +"; +const SAMPLE_OUTPUT: &str = "\ +0 +0 +0 +1 +0 +1 +0 +1 +"; + +fn dfs( + graph: &Graph, + u: usize, + l: &mut [usize], + r: &mut [usize], + p: &mut [usize], + time: &mut usize, +) { + *time += 1; + l[u] = *time; + + for (_, v) in graph.adj_list(u) { + if l[v] == 0 { + p[v] = l[u]; + dfs(graph, v, l, r, p, time); + } + } + + r[u] = *time; +} + +fn main1() { + let cursor = Cursor::new(SAMPLE_INPUT); + let mut scan = Scanner::new(cursor); + let mut out = String::new(); + + let n = scan.next::(); + let mut tree = Graph::new(n, 2 * (n - 1)); + for _ in 1..n { + let u = scan.next::() - 1; + let v = scan.next::() - 1; + tree.add_undirected_edge(u, v); + } + + let mut l = vec![0; n]; + let mut r = vec![0; n]; + let mut p = vec![0; n]; + dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); + + let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); + let q = scan.next::(); + for _ in 0..q { + let c = scan.next::(); + let v = scan.next::() - 1; + let (p, l, r) = (p[v], l[v], r[v]); + let len = (1 + r - l) as i64; + let full = arq.query(l, r).0 == len; + if c == 1 { + if !full { + arq.modify(p, p, &0); + arq.modify(l, r, &1); + } + } else if c == 2 { + arq.modify(l, l, &0); + } else { + writeln!(&mut out, "{}", if full { 1 } else { 0 }).unwrap(); + } + } + + assert_eq!(out, SAMPLE_OUTPUT); +} + +#[test] +fn main() { + // If your contest solution requires a lot of stack space, make sure to + // run it in a custom thread like this. + std::thread::Builder::new() + .stack_size(50_000_000) + .spawn(main1) + .unwrap() + .join() + .unwrap(); +} diff --git a/tests/example_problem.rs b/tests/example_problem.rs deleted file mode 100644 index 0760214..0000000 --- a/tests/example_problem.rs +++ /dev/null @@ -1,28 +0,0 @@ -// To make a self-contained file for contest submission, dump each desired -// module's contents directly here instead of these use statements. -extern crate algorithms; -use algorithms::scanner::*; -use algorithms::graph::*; - -fn main1() { - let stdin = std::io::stdin(); - let mut scan = Scanner::new(stdin.lock()); - let mut graph = Graph::new(1, 0); - for _ in 0..0 { - let u = scan.next::(); - let v = scan.next::(); - graph.add_edge(u, v); - } -} - -#[test] -fn main() { - // If your contest solution requires a lot of stack space, make sure to - // run it in a custom thread. - std::thread::Builder::new() - .stack_size(50_000_000) - .spawn(main1) - .unwrap() - .join() - .unwrap(); -} From 8c9070d41d5d2c8977cf501c43c1fc9e3606a8a6 Mon Sep 17 00:00:00 2001 From: EbTech Date: Tue, 4 Jul 2017 17:26:42 -0700 Subject: [PATCH 06/71] Removed redundant len from Water Tree solution --- tests/codeforces343d.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 7cabd81..c90e14e 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -85,8 +85,7 @@ fn main1() { let c = scan.next::(); let v = scan.next::() - 1; let (p, l, r) = (p[v], l[v], r[v]); - let len = (1 + r - l) as i64; - let full = arq.query(l, r).0 == len; + let full = arq.query(l, r).0 == arq.query(l, r).1; if c == 1 { if !full { arq.modify(p, p, &0); From 0d83d5e6d47c0ae64c34c099e55c4cc5847804dd Mon Sep 17 00:00:00 2001 From: EbTech Date: Tue, 4 Jul 2017 22:02:55 -0700 Subject: [PATCH 07/71] Call arq.query only once --- src/arq_tree.rs | 1 - tests/codeforces343d.rs | 18 ++++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index aa6a7ec..070148b 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -147,7 +147,6 @@ impl ArqSpec for AssignAdd { } } - #[cfg(test)] mod test { use super::*; diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index c90e14e..efe005a 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,8 +3,6 @@ //! module's contents directly here instead of the use statements. //! Also, replace io::Cursor with io::stdin as shown in scanner.rs. extern crate algorithms; -use std::fmt::Write as FmtWrite; -use std::io::Cursor; use algorithms::arq_tree::{ArqTree, AssignAdd}; use algorithms::graph::Graph; use algorithms::scanner::Scanner; @@ -62,7 +60,7 @@ fn dfs( } fn main1() { - let cursor = Cursor::new(SAMPLE_INPUT); + let cursor = std::io::Cursor::new(SAMPLE_INPUT); let mut scan = Scanner::new(cursor); let mut out = String::new(); @@ -84,17 +82,17 @@ fn main1() { for _ in 0..q { let c = scan.next::(); let v = scan.next::() - 1; - let (p, l, r) = (p[v], l[v], r[v]); - let full = arq.query(l, r).0 == arq.query(l, r).1; + let (sum, len) = arq.query(l[v], r[v]); if c == 1 { - if !full { - arq.modify(p, p, &0); - arq.modify(l, r, &1); + if sum != len { + arq.modify(p[v], p[v], &0); + arq.modify(l[v], r[v], &1); } } else if c == 2 { - arq.modify(l, l, &0); + arq.modify(l[v], l[v], &0); } else { - writeln!(&mut out, "{}", if full { 1 } else { 0 }).unwrap(); + use std::fmt::Write; + writeln!(&mut out, "{}", if sum == len { 1 } else { 0 }).unwrap(); } } From 078e3e1283b9e83c9c97ed654288dcaf1dfad046 Mon Sep 17 00:00:00 2001 From: EbTech Date: Thu, 6 Jul 2017 09:18:44 -0700 Subject: [PATCH 08/71] Documenting panics --- src/arq_tree.rs | 10 +++++++--- src/graph/connectivity.rs | 19 +++++++++++++------ src/graph/flow.rs | 25 +++++++++++++++++++------ src/graph/mod.rs | 24 +++++++++++++++--------- src/math.rs | 13 +++++++++++-- src/scanner.rs | 4 ++++ src/string_proc.rs | 8 ++++++++ 7 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 070148b..92ffc07 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -60,7 +60,7 @@ where } } - /// Performs the endomorphism f on all entries from l to r, inclusive. + /// Applies the endomorphism f to all entries from l to r, inclusive. pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { l += self.d.len(); r += self.d.len(); @@ -113,9 +113,9 @@ pub trait ArqSpec { fn compose(f: &Self::F, g: &Self::F) -> Self::F; /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) fn apply(f: &Self::F, a: &Self::M) -> Self::M; - /// Require for alla,b,c: op(a, op(b, c)) = op(op(a, b), c) + /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) fn op(a: &Self::M, b: &Self::M) -> Self::M; - /// Require all a: op(a, identity()) = op(identity(), a) = a + /// Require for all a: op(a, identity()) = op(identity(), a) = a fn identity() -> Self::M; } @@ -128,6 +128,10 @@ pub trait ArqSpec { /// of a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism: /// f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) = (c*s,s)+(c*t,t) = /// f_c((a,s)) + f_c((b,t)). +/// +/// # Panics +/// +/// Associated functions will panic on overflow. pub struct AssignAdd; impl ArqSpec for AssignAdd { diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index d1246ca..3e7bf07 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -9,19 +9,25 @@ use std::cmp::min; /// - 2-edge-connected components (2ECC), /// - 2-vertex-connected components (2VCC) /// -/// Multiple-edges and self-loops should be correctly handled. +/// Multiple-edges and self-loops are correctly handled. pub struct ConnectivityGraph<'a> { - pub graph: &'a Graph, // Immutable graph, frozen for lifetime of this obj. - pub cc: Vec, // Stores id of a vertex's CC, SCC or 2ECC. - pub vcc: Vec, // Stores id of an edge's 2VCC. + // Immutable graph, frozen for the lifetime of the ConnectivityGraph object. + pub graph: &'a Graph, + /// ID of a vertex's CC, SCC or 2ECC (new() documents when each applies). + pub cc: Vec, + /// ID of an edge's 2VCC, where applicable. + pub vcc: Vec, + /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, + /// Total number of 2VCCs, where applicable. pub num_vcc: usize, } impl<'a> ConnectivityGraph<'a> { /// Computes the SCCs of a directed graph in reverse topological order, or /// the 2ECCs/2VCCS of an undirected graph. Can also get CCs by passing an - /// undirected graph using `is_directed == true`. + /// undirected graph using `is_directed == true`. Undefined behavior if + /// passing a directed graph using `is_directed == false`. pub fn new(graph: &'a Graph, is_directed: bool) -> Self { let mut connect = Self { graph: graph, @@ -102,7 +108,8 @@ impl<'a> ConnectivityGraph<'a> { .collect() } - /// Gets the vertices of a directed acyclic graph (DAG) in topological order. + /// Gets the vertices of a directed acyclic graph (DAG) in topological + /// order. Undefined behavior if the graph is not a DAG. pub fn topological_sort(&self) -> Vec { let mut vertices = (0..self.graph.num_v()).collect::>(); vertices.sort_by_key(|&u| self.num_cc - self.cc[u]); diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 9e02c69..4b6c628 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,12 +1,15 @@ -//! Maximum flows and minimum cuts. +//! Maximum flows, matchings, and minimum cuts. use graph::{Graph, AdjListIterator}; use std::cmp::min; const INF: i64 = 0x3f3f3f3f; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { - pub graph: Graph, // Owned graph, controlled by this FlowGraph object. + /// Owned graph, managed by this FlowGraph object. + pub graph: Graph, + /// Edge capacities. pub cap: Vec, + /// Edge cost per unit flow. pub cost: Vec, } @@ -30,9 +33,14 @@ impl FlowGraph { self.graph.add_undirected_edge(u, v); } - /// Dinic's maximum flow / Hopcroft-Karp maximum bipartite matching: + /// Dinic's algorithm to find the maximum flow from s to t where s != t. + /// Generalizes the Hopcroft-Karp maximum bipartite matching algorithm. /// V^2E in general, min(V^(2/3),sqrt(E))E when all edges are unit capacity, /// sqrt(V)E when all vertices are unit capacity as in bipartite graphs. + /// + /// # Panics + /// + /// Panics if the maximum flow is 2^63 or larger. pub fn dinic(&self, s: usize, t: usize) -> i64 { let mut flow = vec![0; self.graph.num_e()]; let mut max_flow = 0; @@ -110,7 +118,12 @@ impl FlowGraph { .collect() } - /// Minimum cost maximum flow, assuming no negative-cost cycles. + /// Among all s-t maximum flows, finds one with minimum cost, assuming + /// s != t and no negative-cost cycles. + /// + /// # Panics + /// + /// Panics if the flow or cost overflow a 64-bit signed integer. pub fn mcf(&self, s: usize, t: usize) -> (i64, i64) { let mut pot = vec![0; self.graph.num_v()]; @@ -139,8 +152,8 @@ impl FlowGraph { (min_cost, max_flow) } - // Maintains Johnson's potentials to prevent negative-weight residual edges. - // This enables running Dijkstra instead of the slower Bellman-Ford. + // Maintains Johnson's potentials to prevent negative-cost residual edges. + // This allows running Dijkstra instead of the slower Bellman-Ford. fn mcf_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { let mut vis = vec![false; self.graph.num_v()]; let mut dist = vec![INF; self.graph.num_v()]; diff --git a/src/graph/mod.rs b/src/graph/mod.rs index b9782e1..9152894 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -1,4 +1,8 @@ -//! Basic graph library without explicit support for deletion. +//! Basic graph module without explicit support for deletion. +//! +//! # Panics +//! +//! All methods will panic if given an out-of-bounds element index. pub mod flow; pub mod connectivity; @@ -76,9 +80,10 @@ impl Graph { self.add_edge(v, u); } - /// If we think of each even-numbered vertex as a variable, and its successor - /// as its negation, then we can build the implication graph corresponding - /// to any 2-CNF formula. Note that u||v == !u -> v == !v -> u. + /// If we think of each even-numbered vertex as a variable, and its + /// odd-numbered successor as its negation, then we can build the + /// implication graph corresponding to any 2-CNF formula. + /// Note that u||v == !u -> v == !v -> u. pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { self.add_edge(u ^ 1, v); self.add_edge(v ^ 1, u); @@ -92,9 +97,10 @@ impl Graph { } } - /// Finds the sequence of edges in an Euler path starting from u, assuming it - /// exists and that the graph is directed. To extend this to undirected - /// graphs, keep track of a visited array to skip the reverse edge. + /// Finds the sequence of edges in an Euler path starting from u, assuming + /// it exists and that the graph is directed. Undefined behavior if this + /// precondition is violated. To extend this to undirected graphs, maintain + /// a visited array to skip the reverse edge. pub fn euler_path(&self, u: usize) -> Vec { let mut adj_iters = (0..self.num_v()) .map(|u| self.adj_list(u)) @@ -105,8 +111,8 @@ impl Graph { edges } - // Helper function used by euler_path. Note that we can't consume the - // adjacency list in a for loop because recursive calls may need it. + // 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) { while let Some((e, v)) = adj[u].next() { self.euler_recurse(v, adj, edges); diff --git a/src/math.rs b/src/math.rs index 1671e50..8afa3b8 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,7 +1,12 @@ //! Number-theoretic utilities for contest problems. /// Modular exponentiation by repeated squaring: returns base^exp % m. -pub fn mod_pow(mut base: i64, mut exp: i64, m: i64) -> i64 { +/// +/// # Panics +/// +/// Panics if m == 0. May panic on overflow if m * m > 2^63. +pub fn mod_pow(mut base: i64, mut exp: u32, m: u32) -> i64 { + let m = m as i64; let mut result = 1 % m; while exp > 0 { if exp % 2 == 1 { @@ -24,6 +29,10 @@ pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { } /// Assuming a != 0, finds smallest y >= 0 such that ax + by = c. +/// +/// # Panics +/// +/// Panics if a == 0. pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> { let (d, _, yy) = extended_gcd(a, b); if c % d == 0 { @@ -46,7 +55,7 @@ mod test { let base = 31; let base_inv = mod_pow(base, p - 2, p); - let identity = (base * base_inv) % p; + let identity = (base * base_inv) % p as i64; assert_eq!(identity, 1); } diff --git a/src/scanner.rs b/src/scanner.rs index ab384be..06fce7b 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -17,6 +17,10 @@ impl Scanner { } /// Use "turbofish" syntax next::() to select data type of next token. + /// + /// # Panics + /// + /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn next(&mut self) -> T where T::Err: ::std::fmt::Debug, diff --git a/src/string_proc.rs b/src/string_proc.rs index bd026ab..49d179f 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -8,6 +8,10 @@ pub struct Matcher<'a> { impl<'a> Matcher<'a> { /// Sets fail[i] = length of longest proper prefix-suffix of pattern[0...i]. + /// + /// # Panics + /// + /// Panics if pattern is empty. pub fn new(pattern: &'a [u8]) -> Self { let mut fail = Vec::with_capacity(pattern.len()); fail.push(0); @@ -51,6 +55,10 @@ impl<'a> Matcher<'a> { /// Manacher's algorithm for computing palindrome substrings in linear time. /// len[2*i] = odd length of palindrome centred at text[i]. /// len[2*i+1] = even length of palindrome centred at text[i+0.5]. +/// +/// # Panics +/// +/// Panics if text is empty. pub fn palindromes(text: &[u8]) -> Vec { let mut len = Vec::with_capacity(2 * text.len() - 1); len.push(1); From 3d8ad78734007fa91d8427e232c6f542776c2316 Mon Sep 17 00:00:00 2001 From: EbTech Date: Thu, 6 Jul 2017 09:29:36 -0700 Subject: [PATCH 09/71] ArqTree panic documentation --- src/arq_tree.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 92ffc07..203d4bc 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -61,6 +61,10 @@ where } /// Applies the endomorphism f to all entries from l to r, inclusive. + /// + /// # Panics + /// + /// Panics if l or r is out of range. pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { l += self.d.len(); r += self.d.len(); @@ -84,6 +88,10 @@ where } /// Returns the aggregate range query on all entries from l to r, inclusive. + /// + /// # Panics + /// + /// Panics if l or r is out of range. pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { l += self.d.len(); r += self.d.len(); From 8664d3b6ae24c5f4895f18ab3c0328eafbce255b Mon Sep 17 00:00:00 2001 From: EbTech Date: Thu, 6 Jul 2017 09:55:09 -0700 Subject: [PATCH 10/71] ArqSpec for Range Minimum Query --- src/arq_tree.rs | 47 ++++++++++++++++++++++++++++++++++++----- tests/codeforces343d.rs | 4 ++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 203d4bc..f89179a 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -140,9 +140,9 @@ pub trait ArqSpec { /// # Panics /// /// Associated functions will panic on overflow. -pub struct AssignAdd; +pub struct AssignSum; -impl ArqSpec for AssignAdd { +impl ArqSpec for AssignSum { type F = i64; type M = (i64, i64); fn compose(f: &Self::F, _: &Self::F) -> Self::F { @@ -159,13 +159,37 @@ impl ArqSpec for AssignAdd { } } +/// Range Minimum Query, a classic form of associative range query. +// Exercises: try augmenting this struct to find the index of a minimum element +// in a range query, as well as the number of elements that match the minimum. +// Then instead of assigning to a range, try to support the operation of +// incrementing each element in a range by a given offset! +pub struct AssignMin; + +impl ArqSpec for AssignMin { + type F = i64; + type M = i64; + fn compose(f: &Self::F, _: &Self::F) -> Self::F { + *f + } + fn apply(f: &Self::F, _: &Self::M) -> Self::M { + *f + } + fn op(a: &Self::M, b: &Self::M) -> Self::M { + ::std::cmp::min(*a, *b) + } + fn identity() -> Self::M { + Self::M::max_value() + } +} + #[cfg(test)] mod test { use super::*; - + #[test] - fn test_arq_tree() { - let mut arq = ArqTree::::new(vec![(0, 1); 10]); + fn test_range_sum() { + let mut arq = ArqTree::::new(vec![(0, 1); 10]); assert_eq!(arq.query(0, 9), (0, 10)); @@ -174,4 +198,17 @@ mod test { assert_eq!(arq.query(0, 9), (23, 10)); } + + #[test] + fn test_rmq() { + let mut arq = ArqTree::::new(vec![0; 10]); + + assert_eq!(arq.query(0, 9), 0); + + arq.modify(2, 4, &-5); + arq.modify(5, 7, &-3); + arq.modify(1, 6, &1); + + assert_eq!(arq.query(0, 9), -3); + } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index efe005a..b817d8c 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,7 +3,7 @@ //! module's contents directly here instead of the use statements. //! Also, replace io::Cursor with io::stdin as shown in scanner.rs. extern crate algorithms; -use algorithms::arq_tree::{ArqTree, AssignAdd}; +use algorithms::arq_tree::{ArqTree, AssignSum}; use algorithms::graph::Graph; use algorithms::scanner::Scanner; @@ -77,7 +77,7 @@ fn main1() { let mut p = vec![0; n]; dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); - let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); + let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); let q = scan.next::(); for _ in 0..q { let c = scan.next::(); From 1bf6b15e9fb116e7841fbc16f29b0fdba473a8d2 Mon Sep 17 00:00:00 2001 From: EbTech Date: Thu, 6 Jul 2017 10:25:33 -0700 Subject: [PATCH 11/71] Document reasoning behind not using function pointers --- src/arq_tree.rs | 17 +++++++++++------ src/string_proc.rs | 32 ++++++++++++++++---------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index f89179a..229c160 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -115,7 +115,12 @@ where } pub trait ArqSpec { + /// Type of data representing an endomorphism. + // Note that while a Fn(M) -> M may seem like a more natural representation + // for an endomorphism, compositions would then have to delegate to each of + // their parts. This representation is more efficient. type F; + /// Type of monoid elements. type M; /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) fn compose(f: &Self::F, g: &Self::F) -> Self::F; @@ -129,13 +134,13 @@ pub trait ArqSpec { /// In this example, we want to support range sum queries and range constant /// assignments. Note that constant assignment f_c(a) = c is not a endomorphism -/// on integers because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive +/// on (i64, +) because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive /// terms, the problem is that the internal nodes of the tree should really be /// set to a multiple of c, corresponding to the subtree size. So let's augment /// the monoid type with size information, using the 2D vector (a_i,1) instead -/// of a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism: -/// f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) = (c*s,s)+(c*t,t) = -/// f_c((a,s)) + f_c((b,t)). +/// of a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism on +/// vector addition: f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) +/// = (c*s,s)+(c*t,t) = f_c((a,s)) + f_c((b,t)). /// /// # Panics /// @@ -186,7 +191,7 @@ impl ArqSpec for AssignMin { #[cfg(test)] mod test { use super::*; - + #[test] fn test_range_sum() { let mut arq = ArqTree::::new(vec![(0, 1); 10]); @@ -202,7 +207,7 @@ mod test { #[test] fn test_rmq() { let mut arq = ArqTree::::new(vec![0; 10]); - + assert_eq!(arq.query(0, 9), 0); arq.modify(2, 4, &-5); diff --git a/src/string_proc.rs b/src/string_proc.rs index 49d179f..e27ba85 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -53,37 +53,37 @@ impl<'a> Matcher<'a> { } /// Manacher's algorithm for computing palindrome substrings in linear time. -/// len[2*i] = odd length of palindrome centred at text[i]. -/// len[2*i+1] = even length of palindrome centred at text[i+0.5]. +/// pal[2*i] = odd length of palindrome centred at text[i]. +/// pal[2*i+1] = even length of palindrome centred at text[i+0.5]. /// /// # Panics /// /// Panics if text is empty. pub fn palindromes(text: &[u8]) -> Vec { - let mut len = Vec::with_capacity(2 * text.len() - 1); - len.push(1); - while len.len() < len.capacity() { - let i = len.len() - 1; - let max_len = ::std::cmp::min(i + 1, len.capacity() - i); - while len[i] < max_len && text[(i - len[i] - 1) / 2] == text[(i + len[i] + 1) / 2] { - len[i] += 2; + let mut pal = Vec::with_capacity(2 * text.len() - 1); // only mutable var! + pal.push(1); + while pal.len() < pal.capacity() { + let i = pal.len() - 1; + let max_len = ::std::cmp::min(i + 1, pal.capacity() - i); + while pal[i] < max_len && text[(i - pal[i] - 1) / 2] == text[(i + pal[i] + 1) / 2] { + pal[i] += 2; } - if len[i] < 2 { - let a = 1 - len[i]; - len.push(a); + if pal[i] < 2 { + let a = 1 - pal[i]; + pal.push(a); } else { for d in 1.. { - let (a, b) = (len[i - d], len[i] - d); + let (a, b) = (pal[i - d], pal[i] - d); if a < b { - len.push(a); + pal.push(a); } else { - len.push(b); + pal.push(b); break; } } } } - len + pal } #[cfg(test)] From 5c60b316b2576d0b7b300e4c9bf35c1e55ac0871 Mon Sep 17 00:00:00 2001 From: EbTech Date: Sun, 17 Sep 2017 21:34:17 -0700 Subject: [PATCH 12/71] Updated to Rust 1.19, the version currently supported by Codeforces --- .travis.yml | 1 + README.md | 10 +++++----- src/arq_tree.rs | 2 +- src/graph/connectivity.rs | 2 +- src/graph/mod.rs | 4 ++++ src/scanner.rs | 2 +- src/string_proc.rs | 5 +---- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c91a74..5be1b36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ rust: - stable - beta - nightly + - 1.19.0 # Current version supported by Codeforces matrix: allow_failures: - rust: nightly diff --git a/README.md b/README.md index e4b1064..3d5df93 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) -A collection of classic data structures and algorithms, emphasizing beauty and clarity over full generality. As such, this should not be viewed as a blackbox *library*, but as a whitebox *cookbook* demonstrating the translation of abstract concepts into executable code. I hope it will be useful to students and educators, as well as competition programmers. +A collection of classic data structures and algorithms, emphasizing beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as competition programmers. This repository is distributed under the [MIT License](LICENSE). The license text need not be included in contest submissions, though I would appreciate linking back to this repo for others to find. Enjoy! @@ -12,15 +12,15 @@ When learning a new algorithm or data structure, it's often helpful to see or pl In addition, the Rust language has outstanding pedagogical attributes. Its compiler acts as a teacher, enforcing strict discipline while pointing to clearer ways to structure one's logic. -## For Competition Programmers +## For Programming Contests -The original intent of this project was to build a reference for use in programming competitions such as [Codeforces](http://codeforces.com) and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests such as [Codeforces](http://codeforces.com) and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. -Most competitive programmers use C/C++ because it allows for fast coding as well as fast execution. However, these languages are notoriously unsafe, wasting a considerable share of the contestant's time and attention on accident prevention and debugging. Java is the next most popular choice, offering a bit of safety at some expense to coding and execution speed. To my delight, I found that Rust provides a lot more safety than Java without the visual clutter, and it's *fast*. A proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! +Most competition programmers use C++ for its fast execution time. However, it's notoriously unsafe, wasting a considerable share of the contestant's time and attention on accident prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to coding and execution speed. To my delight, I found that Rust provides even more safety without the visual clutter, and it's *fast*. A proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! ## Programming Language Advocacy -My other goal is to appeal to developers who feel, as I once did, trapped between the lesser of headaches (e.g., C++ and Java), to raise awareness that *it doesn't have to be this way*. Rather than trying to persuade you with words, this repository aims to show by example and ease the learning curve a bit. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in](https://www.rust-lang.org)! +My other goal is to appeal to developers who feel, as I once did, trapped between the lesser of headaches among old mainstream languages (e.g., C++/Java), to raise awareness that *it doesn't have to be this way*. Rather than trying to persuade you with words, this repository aims to show by example and ease the learning curve. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/second-edition/) ## Contents diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 229c160..0066302 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -26,7 +26,7 @@ where } Self { d: vec![None; size], - t: t, + t, } } diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 3e7bf07..2d65a03 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -30,7 +30,7 @@ impl<'a> ConnectivityGraph<'a> { /// passing a directed graph using `is_directed == false`. pub fn new(graph: &'a Graph, is_directed: bool) -> Self { let mut connect = Self { - graph: graph, + graph, cc: vec![0; graph.num_v()], vcc: vec![0; graph.num_e()], num_cc: 0, diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 9152894..fb52d82 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -38,9 +38,13 @@ impl DisjointSets { } /// A compact graph representation. Edges are numbered in order of insertion. +/// Each adjacency list consists of all edges pointing out from a given vertex. pub struct Graph { + /// Maps a vertex id to the first edge in its adjacency list. first: Vec>, + /// Maps an edge id to the next edge in the same adjacency list. next: Vec>, + /// Maps an edge id to the vertex that it points to. endp: Vec, } diff --git a/src/scanner.rs b/src/scanner.rs index 06fce7b..aa9df09 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -11,7 +11,7 @@ pub struct Scanner { impl Scanner { pub fn new(reader: B) -> Self { Self { - reader: reader, + reader, buffer: Vec::new(), } } diff --git a/src/string_proc.rs b/src/string_proc.rs index e27ba85..2b51117 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -25,10 +25,7 @@ impl<'a> Matcher<'a> { } fail.push(len); } - Self { - pattern: pattern, - fail: fail, - } + Self { pattern, fail } } /// KMP algorithm, sets matches[i] = length of longest prefix of pattern From a32cddc6b99939a83a297d2bc96a2968d20ff4fc Mon Sep 17 00:00:00 2001 From: EbTech Date: Thu, 2 Nov 2017 23:45:38 -0700 Subject: [PATCH 13/71] Updated to Rust 1.21 --- .travis.yml | 2 +- README.md | 8 +++-- src/arq_tree.rs | 63 ++++++++++++++++++++------------------- src/graph/connectivity.rs | 9 +++--- src/graph/flow.rs | 9 +++--- src/graph/mod.rs | 2 +- src/string_proc.rs | 2 +- 7 files changed, 49 insertions(+), 46 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5be1b36..eea7ae4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ rust: - stable - beta - nightly - - 1.19.0 # Current version supported by Codeforces + - 1.21.0 # Current version supported by Codeforces matrix: allow_failures: - rust: nightly diff --git a/README.md b/README.md index 3d5df93..96810f0 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,15 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi The original intent of this project was to build a reference for use in programming contests such as [Codeforces](http://codeforces.com) and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. -Most competition programmers use C++ for its fast execution time. However, it's notoriously unsafe, wasting a considerable share of the contestant's time and attention on accident prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to coding and execution speed. To my delight, I found that Rust provides even more safety without the visual clutter, and it's *fast*. A proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! +Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. + +To my delight, I found that Rust provides even more safety without the visual clutter, and it's *fast*. A proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! ## Programming Language Advocacy -My other goal is to appeal to developers who feel, as I once did, trapped between the lesser of headaches among old mainstream languages (e.g., C++/Java), to raise awareness that *it doesn't have to be this way*. Rather than trying to persuade you with words, this repository aims to show by example and ease the learning curve. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/second-edition/) +My other goal is to appeal to developers who feel, as I once did, trapped between the lesser of headaches among old mainstream languages (e.g., C++/Java), to raise awareness that *it doesn't have to be this way*. + +Rather than try to persuade you with words, this repository aims to show by example while easing the learning curve. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/second-edition/) ## Contents diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 0066302..fbeba38 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -132,13 +132,38 @@ pub trait ArqSpec { fn identity() -> Self::M; } -/// In this example, we want to support range sum queries and range constant -/// assignments. Note that constant assignment f_c(a) = c is not a endomorphism -/// on (i64, +) because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive -/// terms, the problem is that the internal nodes of the tree should really be -/// set to a multiple of c, corresponding to the subtree size. So let's augment -/// the monoid type with size information, using the 2D vector (a_i,1) instead -/// of a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism on +/// Range Minimum Query, a classic application of associative range query. +// Exercises: try augmenting this struct to find the index of a minimum element +// in a range query, as well as the number of elements that match the minimum. +// Then instead of assigning to a range, try to support the operation of +// incrementing each element in a range by a given offset! +pub struct AssignMin; + +impl ArqSpec for AssignMin { + type F = i64; + type M = i64; + fn compose(f: &Self::F, _: &Self::F) -> Self::F { + *f + } + fn apply(f: &Self::F, _: &Self::M) -> Self::M { + *f + } + fn op(a: &Self::M, b: &Self::M) -> Self::M { + (*a).min(*b) + } + fn identity() -> Self::M { + Self::M::max_value() + } +} + +/// A slightly more advanced application, where we aim to support range sum +/// queries and range constant assignments. +/// Note that constant assignment f_c(a) = c is not a endomorphism on (i64, +) +/// because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive terms, the +/// problem is that the internal nodes of the tree should not simply be set to +/// c, but rather, to c multiplied by the subtree size. So let's augment the +/// monoid type with size information, using the 2D vector (a_i,1) instead of +/// a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism on /// vector addition: f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) /// = (c*s,s)+(c*t,t) = f_c((a,s)) + f_c((b,t)). /// @@ -164,30 +189,6 @@ impl ArqSpec for AssignSum { } } -/// Range Minimum Query, a classic form of associative range query. -// Exercises: try augmenting this struct to find the index of a minimum element -// in a range query, as well as the number of elements that match the minimum. -// Then instead of assigning to a range, try to support the operation of -// incrementing each element in a range by a given offset! -pub struct AssignMin; - -impl ArqSpec for AssignMin { - type F = i64; - type M = i64; - fn compose(f: &Self::F, _: &Self::F) -> Self::F { - *f - } - fn apply(f: &Self::F, _: &Self::M) -> Self::M { - *f - } - fn op(a: &Self::M, b: &Self::M) -> Self::M { - ::std::cmp::min(*a, *b) - } - fn identity() -> Self::M { - Self::M::max_value() - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 2d65a03..2d8b4db 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,6 +1,5 @@ //! Graph connectivity structures. use graph::Graph; -use std::cmp::min; /// Represents the decomposition of a graph into any of its constituent parts: /// @@ -78,7 +77,7 @@ impl<'a> ConnectivityGraph<'a> { self.scc(v, t, vis, low, v_stack); } if self.cc[v] == 0 { - low[u] = min(low[u], low[v]); + low[u] = low[u].min(low[v]); } } if vis[u] == low[u] { @@ -112,7 +111,7 @@ impl<'a> ConnectivityGraph<'a> { /// order. Undefined behavior if the graph is not a DAG. pub fn topological_sort(&self) -> Vec { let mut vertices = (0..self.graph.num_v()).collect::>(); - vertices.sort_by_key(|&u| self.num_cc - self.cc[u]); + vertices.sort_unstable_by_key(|&u| self.num_cc - self.cc[u]); vertices } @@ -134,7 +133,7 @@ impl<'a> ConnectivityGraph<'a> { if vis[v] == 0 { e_stack.push(e); self.bcc(v, e, t, vis, low, v_stack, e_stack); - low[u] = min(low[u], low[v]); + low[u] = low[u].min(low[v]); if vis[u] <= low[v] { // u is a cut vertex unless it's a one-child root self.num_vcc += 1; @@ -147,7 +146,7 @@ impl<'a> ConnectivityGraph<'a> { } } } else if vis[v] < vis[u] && e ^ par != 1 { - low[u] = min(low[u], vis[v]); + low[u] = low[u].min(vis[v]); e_stack.push(e); } else if v == u { // e is a self-loop diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 4b6c628..37f0154 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,7 +1,6 @@ //! Maximum flows, matchings, and minimum cuts. use graph::{Graph, AdjListIterator}; -use std::cmp::min; -const INF: i64 = 0x3f3f3f3f; +const INF: i64 = 0x3f3f3f3f3f3f3f3f; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { @@ -91,7 +90,7 @@ impl FlowGraph { let mut df = 0; while let Some(&(e, v)) = adj[u].peek() { - let rem_cap = min(self.cap[e] - flow[e], f - df); + let rem_cap = (self.cap[e] - flow[e]).min(f - df); if rem_cap > 0 && dist[v] == dist[u] + 1 { let cf = self.dinic_augment(v, t, rem_cap, dist, adj, flow); flow[e] += cf; @@ -133,7 +132,7 @@ impl FlowGraph { if self.cap[e] > 0 { let u = self.graph.endp[e ^ 1]; let v = self.graph.endp[e]; - pot[v] = min(pot[v], pot[u] + self.cost[e]); + pot[v] = pot[v].min(pot[u] + self.cost[e]); } } } @@ -181,7 +180,7 @@ impl FlowGraph { let (mut dc, mut df) = (0, INF); let mut u = t; while let Some(e) = par[u] { - df = min(df, self.cap[e] - flow[e]); + df = df.min(self.cap[e] - flow[e]); u = self.graph.endp[e ^ 1]; } u = t; diff --git a/src/graph/mod.rs b/src/graph/mod.rs index fb52d82..414fd41 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -128,7 +128,7 @@ impl Graph { pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { assert_eq!(self.num_e(), 2 * weights.len()); let mut edges = (0..weights.len()).collect::>(); - edges.sort_by_key(|&e| weights[e]); + edges.sort_unstable_by_key(|&e| weights[e]); let mut components = DisjointSets::new(self.num_v()); edges diff --git a/src/string_proc.rs b/src/string_proc.rs index 2b51117..e7e4cba 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -61,7 +61,7 @@ pub fn palindromes(text: &[u8]) -> Vec { pal.push(1); while pal.len() < pal.capacity() { let i = pal.len() - 1; - let max_len = ::std::cmp::min(i + 1, pal.capacity() - i); + let max_len = (i + 1).min(pal.capacity() - i); while pal[i] < max_len && text[(i - pal[i] - 1) / 2] == text[(i + pal[i] + 1) / 2] { pal[i] += 2; } From 339bab69cd9c9e3c31c0399d0950054e2c641bd2 Mon Sep 17 00:00:00 2001 From: EbTech Date: Sat, 11 Nov 2017 00:25:14 -0800 Subject: [PATCH 14/71] Added O(nlogn) suffix array construction using counting sort --- README.md | 10 ++-- src/string_proc.rs | 126 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 96810f0..9d779e5 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,17 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests such as [Codeforces](http://codeforces.com) and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests such as [Codeforces](http://codeforces.com), [Hackerrank](https://www.hackerrank.com/), and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. -To my delight, I found that Rust provides even more safety without the visual clutter, and it's *fast*. A proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! +To my delight, I found that Rust provides even more bug-safety without the visual clutter, and it's *fast*. A proficient Rust programmer might stand to gain a competitive advantage as well as a more pleasant experience! + +Note that the online judges [SPOJ](http://www.spoj.com/) and [Timus](http://acm.timus.ru/) also support submissions in Rust. As of this writing, they use older compilers which might reject certain features used in this cookbook. ## Programming Language Advocacy -My other goal is to appeal to developers who feel, as I once did, trapped between the lesser of headaches among old mainstream languages (e.g., C++/Java), to raise awareness that *it doesn't have to be this way*. +My other goal is to appeal to developers who feel limited by older mainstream languages, to raise awareness that *it doesn't have to be this way*. Rather than try to persuade you with words, this repository aims to show by example while easing the learning curve. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/second-edition/) @@ -34,4 +36,4 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Associative range query](src/arq_tree.rs): known colloquially as *segtrees* - [Math](src/math.rs): Euclid's GCD algorithm, Bezout's identity - [Scanner](src/scanner.rs): utility for reading input data -- [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, Manacher's palindrome search +- [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search diff --git a/src/string_proc.rs b/src/string_proc.rs index e7e4cba..a083dfe 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -2,12 +2,15 @@ /// Data structure for Knuth-Morris-Pratt string matching against a pattern. pub struct Matcher<'a> { + /// The string pattern to search for. pub pattern: &'a [u8], + /// KMP match failure automaton. fail[i] is the length of the longest + /// proper prefix-suffix of pattern[0...i]. pub fail: Vec, } impl<'a> Matcher<'a> { - /// Sets fail[i] = length of longest proper prefix-suffix of pattern[0...i]. + /// Precomputes the automaton that allows linear-time string matching. /// /// # Panics /// @@ -49,6 +52,87 @@ impl<'a> Matcher<'a> { } } +/// Suffix array data structure, useful for a variety of string queries. +pub struct SuffixArray { + /// The suffix array itself, holding suffix indices in sorted order. + pub sfx: Vec, + /// rank[i][j] = rank of the j'th suffix, considering only 2^i chars. + pub rank: Vec>, +} + +impl SuffixArray { + /// O(n + max_key) stable sort on an input that is a permutation of (0..n). + fn counting_sort(p_gen: I, keys: &[usize], max_key: usize) -> Vec + where + I: DoubleEndedIterator, + { + let mut counts = vec![0; max_key]; + for &k in keys { + counts[k] += 1; + } + let mut total = 0; + for c in counts.iter_mut() { + total += *c; + *c = total; + } + let mut result = vec![0; total]; + for p in p_gen.rev() { + let c = &mut counts[keys[p]]; + *c -= 1; + result[*c] = p; + } + result + } + + /// Suffix array construction in O(n log n) time. Makes some unnecessary Vec clones + /// and initializations, so there's room to optimize. + pub fn new(text: &[u8]) -> Self { + let n = text.len(); + let mut rank = vec![text.into_iter().map(|&ch| ch as usize).collect::>()]; + let mut sfx = Self::counting_sort(0..n, rank.last().unwrap(), 256); + // Invariant at the start of every loop iteration: + // suffixes are sorted according to the first skip characters. + for skip in (0..).map(|i| 1 << i).take_while(|&skip| skip < n) { + let prev_rank = rank.last().unwrap().clone(); + let mut cur_rank = prev_rank.clone(); + + let p_gen = (n - skip..n).chain(sfx.into_iter().filter_map(|p| p.checked_sub(skip))); + sfx = Self::counting_sort(p_gen, &prev_rank, n.max(256)); + + let mut prev = sfx[0]; + cur_rank[prev] = 0; + for &p in sfx.iter().skip(1) { + if prev.max(p) + skip < n && prev_rank[prev] == prev_rank[p] && + prev_rank[prev + skip] == prev_rank[p + skip] + { + cur_rank[p] = cur_rank[prev]; + } else { + cur_rank[p] = cur_rank[prev] + 1; + } + prev = p; + } + rank.push(cur_rank); + } + Self { sfx, rank } + } + + /// Computes the length of longest common prefix of text[i..] and text[j..]. + pub fn longest_common_prefix(&self, mut i: usize, mut j: usize) -> usize { + let mut len = 0; + for (k, rank) in self.rank.iter().enumerate().rev() { + if rank[i] == rank[j] { + i += 1 << k; + j += 1 << k; + len += 1 << k; + if i.max(j) >= self.sfx.len() { + break; + } + } + } + len + } +} + /// Manacher's algorithm for computing palindrome substrings in linear time. /// pal[2*i] = odd length of palindrome centred at text[i]. /// pal[2*i+1] = even length of palindrome centred at text[i+0.5]. @@ -88,14 +172,44 @@ mod test { use super::*; #[test] - fn test_string() { - let text = "abcbc".as_bytes(); - let pattern = "bc".as_bytes(); + fn test_kmp() { + let text = "banana".as_bytes(); + let pattern = "ana".as_bytes(); let matches = Matcher::new(pattern).kmp_match(text); - assert_eq!(matches, vec![0, 1, 2, 1, 2]); + + assert_eq!(matches, vec![0, 1, 2, 3, 2, 3]); + } + + #[test] + fn test_suffix_array() { + let text1 = "bobocel".as_bytes(); + let text2 = "banana".as_bytes(); + + let sfx1 = SuffixArray::new(text1); + let sfx2 = SuffixArray::new(text2); + + assert_eq!(sfx1.sfx, vec![0, 2, 4, 5, 6, 1, 3]); + assert_eq!(sfx2.sfx, vec![5, 3, 1, 0, 4, 2]); + + assert_eq!(sfx1.longest_common_prefix(0, 2), 2); + assert_eq!(sfx2.longest_common_prefix(1, 3), 3); + + // Check that sfx and rank.last() are essentially inverses of each other. + for (p, &r) in sfx1.rank.last().unwrap().iter().enumerate() { + assert_eq!(sfx1.sfx[r], p); + } + for (p, &r) in sfx2.rank.last().unwrap().iter().enumerate() { + assert_eq!(sfx2.sfx[r], p); + } + } + + #[test] + fn test_palindrome() { + let text = "banana".as_bytes(); let pal_len = palindromes(text); - assert_eq!(pal_len, vec![1, 0, 1, 0, 3, 0, 3, 0, 1]); + + assert_eq!(pal_len, vec![1, 0, 1, 0, 3, 0, 5, 0, 3, 0, 1]); } } From 606ccd6f3c4dbf1f865bd2f22fbf7597935e0ffa Mon Sep 17 00:00:00 2001 From: EbTech Date: Mon, 24 Sep 2018 01:08:43 -0700 Subject: [PATCH 15/71] Updated to Rust 2018 edition with improvements to suffix array, counting sort, and strongly/biconnected comopnents --- .travis.yml | 2 - Cargo.toml | 3 +- README.md | 10 +-- src/graph/connectivity.rs | 141 +++++++++++++++++++------------------- src/graph/flow.rs | 27 ++++---- src/graph/mod.rs | 10 +-- src/math.rs | 5 +- src/scanner.rs | 15 ++-- src/string_proc.rs | 54 ++++++++------- tests/codeforces343d.rs | 12 ++-- 10 files changed, 143 insertions(+), 136 deletions(-) diff --git a/.travis.yml b/.travis.yml index eea7ae4..b8abbfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: rust rust: - - stable - beta - nightly - - 1.21.0 # Current version supported by Codeforces matrix: allow_failures: - rust: nightly diff --git a/Cargo.toml b/Cargo.toml index e10786a..73de399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "algorithms" version = "0.1.0" -authors = ["Aram Ebtekar "] +authors = ["Aram Ebtekar "] +edition = "2018" [dependencies] diff --git a/README.md b/README.md index 9d779e5..c9179da 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,21 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests such as [Codeforces](http://codeforces.com), [Hackerrank](https://www.hackerrank.com/), and the [Google Code Jam](https://code.google.com/codejam). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Hackerrank](https://www.hackerrank.com/). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. To my delight, I found that Rust provides even more bug-safety without the visual clutter, and it's *fast*. A proficient Rust programmer might stand to gain a competitive advantage as well as a more pleasant experience! -Note that the online judges [SPOJ](http://www.spoj.com/) and [Timus](http://acm.timus.ru/) also support submissions in Rust. As of this writing, they use older compilers which might reject certain features used in this cookbook. +Other online judges that support Rust: +- [Timus](http://acm.timus.ru/help.aspx?topic=rust) +- [SPOJ](http://www.spoj.com/) ## Programming Language Advocacy -My other goal is to appeal to developers who feel limited by older mainstream languages, to raise awareness that *it doesn't have to be this way*. +My other goal is to appeal to developers who feel limited by mainstream, arguably outdated, programming languages. Perhaps we have a better way. -Rather than try to persuade you with words, this repository aims to show by example while easing the learning curve. See [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/second-edition/) +Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/2018-edition) ## Contents diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 2d8b4db..1be6917 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,5 +1,40 @@ //! Graph connectivity structures. -use graph::Graph; +use super::Graph; + +/// Helper struct that carries data needed for the depth-first searches in +/// ConnectivityGraph's constructor. +struct ConnectivityData { + time: usize, + vis: Vec, + low: Vec, + v_stack: Vec, + e_stack: Vec, +} + +impl ConnectivityData { + fn new(num_v: usize) -> Self { + Self { + time: 0, + vis: vec![0; num_v], + low: vec![0; num_v], + v_stack: Vec::new(), + e_stack: Vec::new(), + } + } + + fn visit(&mut self, u: usize) { + self.time += 1; + self.vis[u] = self.time; + self.low[u] = self.time; + self.v_stack.push(u); + } + + fn lower(&mut self, u: usize, val: usize) { + if self.low[u] > val { + self.low[u] = val + } + } +} /// Represents the decomposition of a graph into any of its constituent parts: /// @@ -12,7 +47,7 @@ use graph::Graph; pub struct ConnectivityGraph<'a> { // Immutable graph, frozen for the lifetime of the ConnectivityGraph object. pub graph: &'a Graph, - /// ID of a vertex's CC, SCC or 2ECC (new() documents when each applies). + /// ID of a vertex's CC, SCC or 2ECC, whichever applies. pub cc: Vec, /// ID of an edge's 2VCC, where applicable. pub vcc: Vec, @@ -23,10 +58,13 @@ pub struct ConnectivityGraph<'a> { } impl<'a> ConnectivityGraph<'a> { - /// Computes the SCCs of a directed graph in reverse topological order, or - /// the 2ECCs/2VCCS of an undirected graph. Can also get CCs by passing an - /// undirected graph using `is_directed == true`. Undefined behavior if - /// passing a directed graph using `is_directed == false`. + /// Computes CCs (connected components), SCCs (strongly connected + /// components), 2ECCs (2-edge-connected components), and/or 2VCCs + /// (2-vertex-connected components), depending on the parameter and graph: + /// - is_directed == true on directed graph: SCCs in rev-topological order + /// - is_directed == true on undirected graph: CCs + /// - is_directed == false on undirected graph: 2ECCs and 2VCCs + /// - is_directed == false on directed graph: undefined behavior pub fn new(graph: &'a Graph, is_directed: bool) -> Self { let mut connect = Self { graph, @@ -35,54 +73,32 @@ impl<'a> ConnectivityGraph<'a> { num_cc: 0, num_vcc: 0, }; - let mut t = 0; - let mut vis = vec![0; graph.num_v()]; - let mut low = vec![0; graph.num_v()]; - let mut v_stack = Vec::new(); - let mut e_stack = Vec::new(); + let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if vis[u] == 0 { + if data.vis[u] == 0 { if is_directed { - connect.scc(u, &mut t, &mut vis, &mut low, &mut v_stack); + connect.scc(u, &mut data); } else { - connect.bcc( - u, - graph.num_e() + 1, - &mut t, - &mut vis, - &mut low, - &mut v_stack, - &mut e_stack, - ); + connect.bcc(u, graph.num_e() + 1, &mut data); } } } connect } - fn scc( - &mut self, - u: usize, - t: &mut usize, - vis: &mut [usize], - low: &mut [usize], - v_stack: &mut Vec, - ) { - *t += 1; - vis[u] = *t; - low[u] = *t; - v_stack.push(u); + fn scc(&mut self, u: usize, data: &mut ConnectivityData) { + data.visit(u); for (_, v) in self.graph.adj_list(u) { - if vis[v] == 0 { - self.scc(v, t, vis, low, v_stack); + if data.vis[v] == 0 { + self.scc(v, data); } if self.cc[v] == 0 { - low[u] = low[u].min(low[v]); + data.lower(u, data.low[v]); } } - if vis[u] == low[u] { + if data.vis[u] == data.low[u] { self.num_cc += 1; - while let Some(v) = v_stack.pop() { + while let Some(v) = data.v_stack.pop() { self.cc[v] = self.num_cc; if v == u { break; @@ -103,8 +119,7 @@ impl<'a> ConnectivityGraph<'a> { } else { Some(scc_true < scc_false) } - }) - .collect() + }).collect() } /// Gets the vertices of a directed acyclic graph (DAG) in topological @@ -115,29 +130,17 @@ impl<'a> ConnectivityGraph<'a> { vertices } - fn bcc( - &mut self, - u: usize, - par: usize, - t: &mut usize, - vis: &mut [usize], - low: &mut [usize], - v_stack: &mut Vec, - e_stack: &mut Vec, - ) { - *t += 1; - vis[u] = *t; - low[u] = *t; - v_stack.push(u); + fn bcc(&mut self, u: usize, par: usize, data: &mut ConnectivityData) { + data.visit(u); for (e, v) in self.graph.adj_list(u) { - if vis[v] == 0 { - e_stack.push(e); - self.bcc(v, e, t, vis, low, v_stack, e_stack); - low[u] = low[u].min(low[v]); - if vis[u] <= low[v] { + if data.vis[v] == 0 { + data.e_stack.push(e); + self.bcc(v, e, data); + data.lower(u, data.low[v]); + if data.vis[u] <= data.low[v] { // u is a cut vertex unless it's a one-child root self.num_vcc += 1; - while let Some(top_e) = e_stack.pop() { + while let Some(top_e) = data.e_stack.pop() { self.vcc[top_e] = self.num_vcc; self.vcc[top_e ^ 1] = self.num_vcc; if e ^ top_e <= 1 { @@ -145,9 +148,9 @@ impl<'a> ConnectivityGraph<'a> { } } } - } else if vis[v] < vis[u] && e ^ par != 1 { - low[u] = low[u].min(vis[v]); - e_stack.push(e); + } else if data.vis[v] < data.vis[u] && e ^ par != 1 { + data.lower(u, data.vis[v]); + data.e_stack.push(e); } else if v == u { // e is a self-loop self.num_vcc += 1; @@ -155,10 +158,10 @@ impl<'a> ConnectivityGraph<'a> { self.vcc[e ^ 1] = self.num_vcc; } } - if vis[u] == low[u] { + if data.vis[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; - while let Some(v) = v_stack.pop() { + while let Some(v) = data.v_stack.pop() { self.cc[v] = self.num_cc; if v == u { break; @@ -170,9 +173,9 @@ impl<'a> ConnectivityGraph<'a> { /// In an undirected graph, determines whether u is an articulation vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { if let Some(first_e) = self.graph.first[u] { - self.graph.adj_list(u).any(|(e, _)| { - self.vcc[first_e] != self.vcc[e] - }) + self.graph + .adj_list(u) + .any(|(e, _)| self.vcc[first_e] != self.vcc[e]) } else { false } diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 37f0154..82b5a8f 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,6 +1,5 @@ //! Maximum flows, matchings, and minimum cuts. -use graph::{Graph, AdjListIterator}; -const INF: i64 = 0x3f3f3f3f3f3f3f3f; +use super::{AdjListIterator, Graph}; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { @@ -13,6 +12,9 @@ pub struct FlowGraph { } impl FlowGraph { + /// An upper limit to the flow. + const INF: i64 = 0x3f3f_3f3f_3f3f_3f3f; + /// Initializes an flow network with vmax vertices and no edges. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { @@ -45,27 +47,27 @@ impl FlowGraph { let mut max_flow = 0; loop { let dist = self.dinic_search(s, &flow); - if dist[t] == INF { + if dist[t] == Self::INF { break; } // Keep track of adjacency lists to avoid revisiting blocked edges. let mut adj_iters = (0..self.graph.num_v()) .map(|u| self.graph.adj_list(u).peekable()) .collect::>(); - max_flow += self.dinic_augment(s, t, INF, &dist, &mut adj_iters, &mut flow); + max_flow += self.dinic_augment(s, t, Self::INF, &dist, &mut adj_iters, &mut flow); } max_flow } // Compute BFS distances to restrict attention to shortest path edges. fn dinic_search(&self, s: usize, flow: &[i64]) -> Vec { - let mut dist = vec![INF; self.graph.num_v()]; + let mut dist = vec![Self::INF; self.graph.num_v()]; let mut q = ::std::collections::VecDeque::new(); dist[s] = 0; q.push_back(s); while let Some(u) = q.pop_front() { for (e, v) in self.graph.adj_list(u) { - if dist[v] == INF && flow[e] < self.cap[e] { + if dist[v] == Self::INF && flow[e] < self.cap[e] { dist[v] = dist[u] + 1; q.push_back(v); } @@ -103,7 +105,7 @@ impl FlowGraph { // The current edge is either saturated or blocked. adj[u].next(); } - return df; + df } /// After running maximum flow, use this to recover the dual minimum cut. @@ -112,9 +114,8 @@ impl FlowGraph { .filter(|&e| { let u = self.graph.endp[e ^ 1]; let v = self.graph.endp[e]; - dist[u] < INF && dist[v] == INF - }) - .collect() + dist[u] < Self::INF && dist[v] == Self::INF + }).collect() } /// Among all s-t maximum flows, finds one with minimum cost, assuming @@ -155,12 +156,12 @@ impl FlowGraph { // This allows running Dijkstra instead of the slower Bellman-Ford. fn mcf_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { let mut vis = vec![false; self.graph.num_v()]; - let mut dist = vec![INF; self.graph.num_v()]; + let mut dist = vec![Self::INF; self.graph.num_v()]; let mut par = vec![None; self.graph.num_v()]; dist[s] = 0; while let Some(u) = (0..self.graph.num_v()) - .filter(|&u| !vis[u] && dist[u] < INF) + .filter(|&u| !vis[u] && dist[u] < Self::INF) .min_by_key(|&u| dist[u] - pot[u]) { vis[u] = true; @@ -177,7 +178,7 @@ impl FlowGraph { // Pushes flow along an augmenting path of minimum cost. fn mcf_augment(&self, t: usize, par: &[Option], flow: &mut [i64]) -> (i64, i64) { - let (mut dc, mut df) = (0, INF); + let (mut dc, mut df) = (0, Self::INF); let mut u = t; while let Some(e) = par[u] { df = df.min(self.cap[e] - flow[e]); diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 414fd41..2330d3b 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -3,8 +3,8 @@ //! # Panics //! //! All methods will panic if given an out-of-bounds element index. -pub mod flow; pub mod connectivity; +pub mod flow; /// Represents a union of disjoint sets. Each set's elements are arranged in a /// tree, whose root is the set's representative. @@ -15,7 +15,9 @@ pub struct DisjointSets { impl DisjointSets { /// Initializes disjoint sets containing one element each. pub fn new(size: usize) -> Self { - Self { parent: (0..size).collect() } + Self { + parent: (0..size).collect(), + } } /// Finds the set's representative. Do path compression along the way to make @@ -133,9 +135,7 @@ impl Graph { let mut components = DisjointSets::new(self.num_v()); edges .into_iter() - .filter(|&e| { - components.merge(self.endp[2 * e], self.endp[2 * e + 1]) - }) + .filter(|&e| components.merge(self.endp[2 * e], self.endp[2 * e + 1])) .collect() } } diff --git a/src/math.rs b/src/math.rs index 8afa3b8..47860dc 100644 --- a/src/math.rs +++ b/src/math.rs @@ -5,8 +5,7 @@ /// # Panics /// /// Panics if m == 0. May panic on overflow if m * m > 2^63. -pub fn mod_pow(mut base: i64, mut exp: u32, m: u32) -> i64 { - let m = m as i64; +pub fn mod_pow(mut base: u64, mut exp: u64, m: u64) -> u64 { let mut result = 1 % m; while exp > 0 { if exp % 2 == 1 { @@ -55,7 +54,7 @@ mod test { let base = 31; let base_inv = mod_pow(base, p - 2, p); - let identity = (base * base_inv) % p as i64; + let identity = (base * base_inv) % p; assert_eq!(identity, 1); } diff --git a/src/scanner.rs b/src/scanner.rs index aa9df09..6a2708b 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -16,12 +16,12 @@ impl Scanner { } } - /// Use "turbofish" syntax next::() to select data type of next token. + /// Use "turbofish" syntax read::() to select data type of next token. /// /// # Panics /// /// Panics if there's an I/O error or if the token cannot be parsed as T. - pub fn next(&mut self) -> T + pub fn read(&mut self) -> T where T::Err: ::std::fmt::Debug, { @@ -31,12 +31,11 @@ impl Scanner { let mut input = String::new(); self.reader.read_line(&mut input).expect("Line not read"); self.buffer = input.split_whitespace().rev().map(String::from).collect(); - self.next() + self.read() } } } - #[cfg(test)] mod test { use super::*; @@ -45,8 +44,8 @@ mod test { fn test_fake_input() { let cursor = io::Cursor::new("44 2"); let mut scan = Scanner::new(cursor); - let x = scan.next::(); - let y = scan.next::(); + let x = scan.read::(); + let y = scan.read::(); assert_eq!(x - y, 42); } @@ -55,7 +54,7 @@ mod test { let stdin = io::stdin(); let mut scan = Scanner::new(stdin.lock()); if false { - let _ = scan.next::(); + let _ = scan.read::(); } } @@ -64,6 +63,6 @@ mod test { fn test_file() { let file = ::std::fs::File::open("asdf.txt").expect("File not found"); let mut scan = Scanner::new(io::BufReader::new(file)); - let _ = scan.next::(); + let _ = scan.read::(); } } diff --git a/src/string_proc.rs b/src/string_proc.rs index a083dfe..df999c2 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -57,59 +57,63 @@ pub struct SuffixArray { /// The suffix array itself, holding suffix indices in sorted order. pub sfx: Vec, /// rank[i][j] = rank of the j'th suffix, considering only 2^i chars. + /// In other words, rank[i] is a ranking of the substrings text[j..j+2^i]. pub rank: Vec>, } impl SuffixArray { - /// O(n + max_key) stable sort on an input that is a permutation of (0..n). - fn counting_sort(p_gen: I, keys: &[usize], max_key: usize) -> Vec - where - I: DoubleEndedIterator, - { + /// O(n + max_key) stable sort on the items generated by vals. + // Items v in vals are sorted according to val_to_key[v]. + fn counting_sort( + vals: impl Iterator + Clone, + val_to_key: &[usize], + max_key: usize, + ) -> Vec { let mut counts = vec![0; max_key]; - for &k in keys { - counts[k] += 1; + for v in vals.clone() { + counts[val_to_key[v]] += 1; } let mut total = 0; for c in counts.iter_mut() { total += *c; - *c = total; + *c = total - *c; } let mut result = vec![0; total]; - for p in p_gen.rev() { - let c = &mut counts[keys[p]]; - *c -= 1; - result[*c] = p; + for v in vals { + let c = &mut counts[val_to_key[v]]; + result[*c] = v; + *c += 1; } result } - /// Suffix array construction in O(n log n) time. Makes some unnecessary Vec clones - /// and initializations, so there's room to optimize. + /// Suffix array construction in O(n log n) time. pub fn new(text: &[u8]) -> Self { let n = text.len(); - let mut rank = vec![text.into_iter().map(|&ch| ch as usize).collect::>()]; - let mut sfx = Self::counting_sort(0..n, rank.last().unwrap(), 256); + let init_rank = text.into_iter().map(|&ch| ch as usize).collect::>(); + let mut sfx = Self::counting_sort(0..n, &init_rank, 256); + let mut rank = vec![init_rank]; // Invariant at the start of every loop iteration: // suffixes are sorted according to the first skip characters. for skip in (0..).map(|i| 1 << i).take_while(|&skip| skip < n) { - let prev_rank = rank.last().unwrap().clone(); + let prev_rank = rank.last().unwrap(); let mut cur_rank = prev_rank.clone(); - let p_gen = (n - skip..n).chain(sfx.into_iter().filter_map(|p| p.checked_sub(skip))); - sfx = Self::counting_sort(p_gen, &prev_rank, n.max(256)); + let pos = (n - skip..n).chain(sfx.into_iter().filter_map(|p| p.checked_sub(skip))); + sfx = Self::counting_sort(pos, &prev_rank, n.max(256)); let mut prev = sfx[0]; cur_rank[prev] = 0; - for &p in sfx.iter().skip(1) { - if prev.max(p) + skip < n && prev_rank[prev] == prev_rank[p] && - prev_rank[prev + skip] == prev_rank[p + skip] + for &cur in sfx.iter().skip(1) { + if prev.max(cur) + skip < n + && prev_rank[prev] == prev_rank[cur] + && prev_rank[prev + skip] == prev_rank[cur + skip] { - cur_rank[p] = cur_rank[prev]; + cur_rank[cur] = cur_rank[prev]; } else { - cur_rank[p] = cur_rank[prev] + 1; + cur_rank[cur] = cur_rank[prev] + 1; } - prev = p; + prev = cur; } rank.push(cur_rank); } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index b817d8c..1e0e349 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -64,11 +64,11 @@ fn main1() { let mut scan = Scanner::new(cursor); let mut out = String::new(); - let n = scan.next::(); + let n = scan.read::(); let mut tree = Graph::new(n, 2 * (n - 1)); for _ in 1..n { - let u = scan.next::() - 1; - let v = scan.next::() - 1; + let u = scan.read::() - 1; + let v = scan.read::() - 1; tree.add_undirected_edge(u, v); } @@ -78,10 +78,10 @@ fn main1() { dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); - let q = scan.next::(); + let q = scan.read::(); for _ in 0..q { - let c = scan.next::(); - let v = scan.next::() - 1; + let c = scan.read::(); + let v = scan.read::() - 1; let (sum, len) = arq.query(l[v], r[v]); if c == 1 { if sum != len { From c101b391dec4ed566238a1ac5e1bef493a604112 Mon Sep 17 00:00:00 2001 From: EbTech Date: Sun, 13 Jan 2019 20:38:27 -0800 Subject: [PATCH 16/71] Scanner is no longer recursive. Const-size vectors replaced by Box<[]> --- .travis.yml | 3 +++ src/graph/connectivity.rs | 24 ++++++++++++------------ src/scanner.rs | 8 ++++---- src/string_proc.rs | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index b8abbfa..382e516 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,10 @@ language: rust rust: + - 1.31.0 # Version currently supported by Codeforces + - stable - beta - nightly matrix: allow_failures: - rust: nightly + fast_finish: true diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 1be6917..42d4bb2 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -5,8 +5,8 @@ use super::Graph; /// ConnectivityGraph's constructor. struct ConnectivityData { time: usize, - vis: Vec, - low: Vec, + vis: Box<[usize]>, + low: Box<[usize]>, v_stack: Vec, e_stack: Vec, } @@ -15,8 +15,8 @@ impl ConnectivityData { fn new(num_v: usize) -> Self { Self { time: 0, - vis: vec![0; num_v], - low: vec![0; num_v], + vis: vec![0; num_v].into_boxed_slice(), + low: vec![0; num_v].into_boxed_slice(), v_stack: Vec::new(), e_stack: Vec::new(), } @@ -77,20 +77,20 @@ impl<'a> ConnectivityGraph<'a> { for u in 0..graph.num_v() { if data.vis[u] == 0 { if is_directed { - connect.scc(u, &mut data); + connect.scc(&mut data, u); } else { - connect.bcc(u, graph.num_e() + 1, &mut data); + connect.bcc(&mut data, u, graph.num_e() + 1); } } } connect } - fn scc(&mut self, u: usize, data: &mut ConnectivityData) { + fn scc(&mut self, data: &mut ConnectivityData, u: usize) { data.visit(u); for (_, v) in self.graph.adj_list(u) { if data.vis[v] == 0 { - self.scc(v, data); + self.scc(data, v); } if self.cc[v] == 0 { data.lower(u, data.low[v]); @@ -122,20 +122,20 @@ impl<'a> ConnectivityGraph<'a> { }).collect() } - /// Gets the vertices of a directed acyclic graph (DAG) in topological - /// order. Undefined behavior if the graph is not a DAG. + /// Gets the vertices of a graph according to a topological order of the + /// strongly connected components. Most often used on DAGs. pub fn topological_sort(&self) -> Vec { let mut vertices = (0..self.graph.num_v()).collect::>(); vertices.sort_unstable_by_key(|&u| self.num_cc - self.cc[u]); vertices } - fn bcc(&mut self, u: usize, par: usize, data: &mut ConnectivityData) { + fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { data.visit(u); for (e, v) in self.graph.adj_list(u) { if data.vis[v] == 0 { data.e_stack.push(e); - self.bcc(v, e, data); + self.bcc(data, v, e); data.lower(u, data.low[v]); if data.vis[u] <= data.low[v] { // u is a cut vertex unless it's a one-child root diff --git a/src/scanner.rs b/src/scanner.rs index 6a2708b..fb8f1b2 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -25,13 +25,13 @@ impl Scanner { where T::Err: ::std::fmt::Debug, { - if let Some(front) = self.buffer.pop() { - front.parse::().expect(&front) - } else { + loop { + if let Some(front) = self.buffer.pop() { + return front.parse::().expect(&front); + } let mut input = String::new(); self.reader.read_line(&mut input).expect("Line not read"); self.buffer = input.split_whitespace().rev().map(String::from).collect(); - self.read() } } } diff --git a/src/string_proc.rs b/src/string_proc.rs index df999c2..ea05b51 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -63,7 +63,7 @@ pub struct SuffixArray { impl SuffixArray { /// O(n + max_key) stable sort on the items generated by vals. - // Items v in vals are sorted according to val_to_key[v]. + /// Items v in vals are sorted according to val_to_key[v]. fn counting_sort( vals: impl Iterator + Clone, val_to_key: &[usize], From 6472de4f9d580b7f3672957c7a770cddeb2bfcbc Mon Sep 17 00:00:00 2001 From: Amila Welihinda Date: Tue, 30 Jan 2018 23:04:04 -0800 Subject: [PATCH 17/71] Create .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target From 88b0d6f394257c0ef1d997f9f15f0a7eedb57b0d Mon Sep 17 00:00:00 2001 From: eric7237cire Date: Thu, 17 Jan 2019 18:13:18 +0100 Subject: [PATCH 18/71] Max bipartite matching (#7) * Adding a Rust iterator example implementing a DFS iteratively * Add a bipartite maximum matching test Refactor dinic to return the actual flow --- Cargo.toml | 1 + src/graph/connectivity.rs | 3 +- src/graph/dfs.rs | 121 ++++++++++++++++++++++++++++++++++++++ src/graph/flow.rs | 65 +++++++++++++++++--- src/graph/mod.rs | 1 + 5 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 src/graph/dfs.rs diff --git a/Cargo.toml b/Cargo.toml index 73de399..debb9ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ authors = ["Aram Ebtekar "] edition = "2018" [dependencies] +bit-vec = "*" diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 42d4bb2..bbbb112 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -119,7 +119,8 @@ impl<'a> ConnectivityGraph<'a> { } else { Some(scc_true < scc_false) } - }).collect() + }) + .collect() } /// Gets the vertices of a graph according to a topological order of the diff --git a/src/graph/dfs.rs b/src/graph/dfs.rs new file mode 100644 index 0000000..d564f79 --- /dev/null +++ b/src/graph/dfs.rs @@ -0,0 +1,121 @@ +use super::Graph; +use crate::graph::AdjListIterator; +use bit_vec::BitVec; + +impl Graph { + pub fn dfs(&self, v: usize) -> DfsIterator { + // Create a stack for DFS + let mut stack: Vec = Vec::new(); + + let adj_iters = (0..self.num_v()) + .map(|u| self.adj_list(u)) + .collect::>(); + + // Push the current source node. + stack.push(v); + + DfsIterator { + visited: BitVec::from_elem(self.num_v(), false), + stack, + adj_iters, + } + } +} +pub struct DfsIterator<'a> { + //is vertex visited + visited: BitVec, + //stack of vertices + stack: Vec, + adj_iters: Vec>, +} + +impl<'a> Iterator for DfsIterator<'a> { + type Item = usize; + + /// Returns next vertex in the DFS + fn next(&mut self) -> Option { + //Sources: + // https://www.geeksforgeeks.org/iterative-depth-first-traversal/ + // https://en.wikipedia.org/wiki/Depth-first_search + while let Some(&s) = self.stack.last() { + + //Does s still have neighbors we need to process? + if let Some((_,s_nbr)) = self.adj_iters[s].next() { + if !self.visited[s_nbr] { + self.stack.push(s_nbr); + } + } else { + //s has no more neighbors, we can pop it off the stack + self.stack.pop(); + } + + // Stack may contain same vertex twice. So + // we return the popped item only + // if it is not visited. + if !self.visited[s] { + self.visited.set(s, true); + return Some(s); + } + } + + None + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_dfs() { + let mut graph = Graph::new(4, 8); + graph.add_edge(0, 2); + graph.add_edge(2, 0); + graph.add_edge(1, 2); + graph.add_edge(0, 1); + graph.add_edge(3, 3); + graph.add_edge(2, 3); + + let dfs_search = graph.dfs(2).collect::>(); + assert_eq!(dfs_search, vec![2, 3, 0, 1]); + } + + #[test] + fn test_dfs2() { + let mut graph = Graph::new(5, 8); + graph.add_edge(0, 2); + graph.add_edge(2, 1); + graph.add_edge(1, 0); + graph.add_edge(0, 3); + graph.add_edge(3, 4); + graph.add_edge(4, 0); + + let dfs_search = graph.dfs(0).collect::>(); + //Note this is not the only valid DFS + assert_eq!(dfs_search, vec![0, 3, 4, 2, 1]); + } + + #[test] + fn test_dfs_space_complexity() { + let num_v = 20; + let mut graph = Graph::new(num_v, 0); + for i in 0..num_v { + for j in 0..num_v { + graph.add_undirected_edge(i, j); + } + } + + let mut dfs_search = graph.dfs(7); + let mut dfs_check = vec![]; + for _ in 0..num_v { + dfs_check.push(dfs_search.next().unwrap()); + assert!(dfs_search.stack.len() <= num_v+1); + } + + dfs_check.sort(); + dfs_check.dedup(); + assert_eq!(0, dfs_check[0]); + assert_eq!(num_v, dfs_check.len()); + assert_eq!(num_v-1, dfs_check[num_v-1]); + } +} diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 82b5a8f..8c52c5d 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -42,7 +42,7 @@ impl FlowGraph { /// # Panics /// /// Panics if the maximum flow is 2^63 or larger. - pub fn dinic(&self, s: usize, t: usize) -> i64 { + pub fn dinic(&self, s: usize, t: usize) -> (i64, Vec) { let mut flow = vec![0; self.graph.num_e()]; let mut max_flow = 0; loop { @@ -56,7 +56,7 @@ impl FlowGraph { .collect::>(); max_flow += self.dinic_augment(s, t, Self::INF, &dist, &mut adj_iters, &mut flow); } - max_flow + (max_flow, flow) } // Compute BFS distances to restrict attention to shortest path edges. @@ -115,7 +115,8 @@ impl FlowGraph { let u = self.graph.endp[e ^ 1]; let v = self.graph.endp[e]; dist[u] < Self::INF && dist[v] == Self::INF - }).collect() + }) + .collect() } /// Among all s-t maximum flows, finds one with minimum cost, assuming @@ -124,7 +125,7 @@ impl FlowGraph { /// # Panics /// /// Panics if the flow or cost overflow a 64-bit signed integer. - pub fn mcf(&self, s: usize, t: usize) -> (i64, i64) { + pub fn mcf(&self, s: usize, t: usize) -> (i64, i64, Vec) { let mut pot = vec![0; self.graph.num_v()]; // Bellman-Ford deals with negative-cost edges at initialization. @@ -149,7 +150,7 @@ impl FlowGraph { min_cost += dc; max_flow += df; } - (min_cost, max_flow) + (min_cost, max_flow, flow) } // Maintains Johnson's potentials to prevent negative-cost residual edges. @@ -205,7 +206,7 @@ mod test { graph.add_edge(0, 1, 4, 1); graph.add_edge(1, 2, 3, 1); - let flow = graph.dinic(0, 2); + let flow = graph.dinic(0, 2).0; assert_eq!(flow, 3); } @@ -217,8 +218,58 @@ mod test { graph.add_edge(2, 3, 7, 8); graph.add_edge(1, 3, 7, 10); - let (cost, flow) = graph.mcf(0, 3); + let (cost, flow, _) = graph.mcf(0, 3); assert_eq!(cost, 18); assert_eq!(flow, 10); } + + #[test] + fn test_max_matching() { + let mut graph = FlowGraph::new(14, 4); + + let source = 0; + let sink = 13; + + //Vertex indices of "left hand side" of bipartite graph go from [left_start, right_start) + let left_start = 1; + //Vertex indices of "right hand side" of bipartite graph go from [right_start, sink) + let right_start = 7; + + //Initialize source / sink connections; both left & right have 6 nodes + for lhs_vertex in left_start..left_start + 6 { + graph.add_edge(source, lhs_vertex, 1, 1); + } + + for rhs_vertex in right_start..right_start + 6 { + graph.add_edge(rhs_vertex, sink, 1, 1); + } + + graph.add_edge(left_start + 0, right_start + 1, 1, 1); + graph.add_edge(left_start + 0, right_start + 2, 1, 1); + graph.add_edge(left_start + 2, right_start + 0, 1, 1); + graph.add_edge(left_start + 2, right_start + 3, 1, 1); + graph.add_edge(left_start + 3, right_start + 2, 1, 1); + graph.add_edge(left_start + 4, right_start + 2, 1, 1); + graph.add_edge(left_start + 4, right_start + 3, 1, 1); + graph.add_edge(left_start + 5, right_start + 5, 1, 1); + + let (flow_amt, flow) = graph.dinic(source, sink); + assert_eq!(flow_amt, 5); + + //L->R edges in maximum matching + let left_right_edges = flow + .into_iter() + .enumerate() + .filter(|&(_e, f)| f > 0) + //map to u->v + .map(|(e, _f)| (graph.graph.endp[e ^ 1], graph.graph.endp[e])) + //leave out source and sink nodes + .filter(|&(u, v)| u != source && v != sink) + .collect::>(); + + assert_eq!( + left_right_edges, + vec![(1, 8), (3, 7), (4, 9), (5, 10), (6, 12)] + ); + } } diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 2330d3b..d0df224 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -4,6 +4,7 @@ //! //! All methods will panic if given an out-of-bounds element index. pub mod connectivity; +mod dfs; pub mod flow; /// Represents a union of disjoint sets. Each set's elements are arranged in a From 61635158f86290011abddbf085537485b4879f4f Mon Sep 17 00:00:00 2001 From: EbTech Date: Mon, 15 Apr 2019 17:13:48 -0700 Subject: [PATCH 19/71] Added prefix trie, eliminated Clippy warnings --- .gitignore | 4 +++- src/graph/dfs.rs | 7 +++--- src/math.rs | 18 ++++++++-------- src/string_proc.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index eb5a316..4308d82 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -target +target/ +**/*.rs.bk +Cargo.lock diff --git a/src/graph/dfs.rs b/src/graph/dfs.rs index d564f79..6c57aa6 100644 --- a/src/graph/dfs.rs +++ b/src/graph/dfs.rs @@ -38,9 +38,8 @@ impl<'a> Iterator for DfsIterator<'a> { // https://www.geeksforgeeks.org/iterative-depth-first-traversal/ // https://en.wikipedia.org/wiki/Depth-first_search while let Some(&s) = self.stack.last() { - //Does s still have neighbors we need to process? - if let Some((_,s_nbr)) = self.adj_iters[s].next() { + if let Some((_, s_nbr)) = self.adj_iters[s].next() { if !self.visited[s_nbr] { self.stack.push(s_nbr); } @@ -109,13 +108,13 @@ mod test { let mut dfs_check = vec![]; for _ in 0..num_v { dfs_check.push(dfs_search.next().unwrap()); - assert!(dfs_search.stack.len() <= num_v+1); + assert!(dfs_search.stack.len() <= num_v + 1); } dfs_check.sort(); dfs_check.dedup(); assert_eq!(0, dfs_check[0]); assert_eq!(num_v, dfs_check.len()); - assert_eq!(num_v-1, dfs_check[num_v-1]); + assert_eq!(num_v - 1, dfs_check[num_v - 1]); } } diff --git a/src/math.rs b/src/math.rs index 47860dc..6380623 100644 --- a/src/math.rs +++ b/src/math.rs @@ -17,28 +17,28 @@ pub fn mod_pow(mut base: u64, mut exp: u64, m: u64) -> u64 { result } -/// Finds (d, x, y) such that d = gcd(a, b) = ax + by. +/// Finds (d, coef_a, coef_b) such that d = gcd(a, b) = a * coef_a + b * coef_b. pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { if b == 0 { (a.abs(), a.signum(), 0) } else { - let (d, x, y) = extended_gcd(b, a % b); - (d, y, x - y * (a / b)) + let (d, coef_a, coef_b) = extended_gcd(b, a % b); + (d, coef_b, coef_a - coef_b * (a / b)) } } -/// Assuming a != 0, finds smallest y >= 0 such that ax + by = c. +/// Assuming a != 0, finds smallest coef_b >= 0 such that a * coef_a + b * coef_b = c. /// /// # Panics /// /// Panics if a == 0. pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> { - let (d, _, yy) = extended_gcd(a, b); + let (d, _, coef_b_init) = extended_gcd(a, b); if c % d == 0 { - let z = (a / d).abs(); - let y = (yy * (c / d) % z + z) % z; - let x = (c - b * y) / a; - Some((d, x, y)) + let a_d = (a / d).abs(); + let coef_b = (coef_b_init * (c / d) % a_d + a_d) % a_d; + let coef_a = (c - b * coef_b) / a; + Some((d, coef_a, coef_b)) } else { None } diff --git a/src/string_proc.rs b/src/string_proc.rs index ea05b51..101c722 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -90,7 +90,7 @@ impl SuffixArray { /// Suffix array construction in O(n log n) time. pub fn new(text: &[u8]) -> Self { let n = text.len(); - let init_rank = text.into_iter().map(|&ch| ch as usize).collect::>(); + let init_rank = text.iter().map(|&ch| ch as usize).collect::>(); let mut sfx = Self::counting_sort(0..n, &init_rank, 256); let mut rank = vec![init_rank]; // Invariant at the start of every loop iteration: @@ -137,6 +137,39 @@ impl SuffixArray { } } +/// Prefix trie +#[derive(Default)] +pub struct Trie { + count: usize, + branches: std::collections::HashMap>, +} + +impl Trie { + /// Inserts a word into the trie. + pub fn insert(&mut self, word: impl IntoIterator) { + let mut node = self; + node.count += 1; + + for ch in word { + node = { node }.branches.entry(ch).or_insert_with(Default::default); + node.count += 1; + } + } + + /// Computes the number of inserted words that start with the given prefix. + pub fn get(&self, prefix: impl IntoIterator) -> usize { + let mut node = self; + + for ch in prefix { + match node.branches.get(&ch) { + Some(sub) => node = sub, + None => return 0, + } + } + node.count + } +} + /// Manacher's algorithm for computing palindrome substrings in linear time. /// pal[2*i] = odd length of palindrome centred at text[i]. /// pal[2*i+1] = even length of palindrome centred at text[i+0.5]. @@ -208,6 +241,24 @@ mod test { } } + #[test] + fn test_trie() { + let dict = vec!["banana", "benefit", "banapple", "ban"]; + + let trie = dict.into_iter().fold(Trie::default(), |mut trie, word| { + Trie::insert(&mut trie, word.bytes()); + trie + }); + + assert_eq!(trie.get("".bytes()), 4); + assert_eq!(trie.get("b".bytes()), 4); + assert_eq!(trie.get("ba".bytes()), 3); + assert_eq!(trie.get("ban".bytes()), 3); + assert_eq!(trie.get("bana".bytes()), 2); + assert_eq!(trie.get("banan".bytes()), 1); + assert_eq!(trie.get("bane".bytes()), 0); + } + #[test] fn test_palindrome() { let text = "banana".as_bytes(); From 191f4efa4e7676609978e8ab306fa02f5b1e6bf0 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 26 May 2019 18:18:59 -0700 Subject: [PATCH 20/71] Scanner::from_file() constructor --- .travis.yml | 2 +- README.md | 4 ++-- src/arq_tree.rs | 4 ++-- src/graph/connectivity.rs | 4 ++-- src/scanner.rs | 21 ++++++++++++--------- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 382e516..c2fe7a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.31.0 # Version currently supported by Codeforces + - 1.31.1 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/README.md b/README.md index c9179da..df691fb 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Hackerrank](https://www.hackerrank.com/). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on making the code concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Hackerrank](https://www.hackerrank.com/) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com/). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. -To my delight, I found that Rust provides even more bug-safety without the visual clutter, and it's *fast*. A proficient Rust programmer might stand to gain a competitive advantage as well as a more pleasant experience! +To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! Other online judges that support Rust: - [Timus](http://acm.timus.ru/help.aspx?topic=rust) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index fbeba38..e736c6e 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -17,10 +17,10 @@ where T::F: Clone, { /// Initializes a static balanced tree on top of the given sequence. - pub fn new(mut init: Vec) -> Self { + pub fn new(init: Vec) -> Self { let size = init.len(); let mut t = (0..size).map(|_| T::identity()).collect::>(); - t.append(&mut init); + t.append(&mut { init }); for i in (0..size).rev() { t[i] = T::op(&t[i << 1], &t[i << 1 | 1]); } diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index bbbb112..2cafc72 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -47,9 +47,9 @@ impl ConnectivityData { pub struct ConnectivityGraph<'a> { // Immutable graph, frozen for the lifetime of the ConnectivityGraph object. pub graph: &'a Graph, - /// ID of a vertex's CC, SCC or 2ECC, whichever applies. + /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, - /// ID of an edge's 2VCC, where applicable. + /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. pub vcc: Vec, /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, diff --git a/src/scanner.rs b/src/scanner.rs index fb8f1b2..5ab98fa 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -21,21 +21,25 @@ impl Scanner { /// # Panics /// /// Panics if there's an I/O error or if the token cannot be parsed as T. - pub fn read(&mut self) -> T - where - T::Err: ::std::fmt::Debug, - { + pub fn read(&mut self) -> T { loop { - if let Some(front) = self.buffer.pop() { - return front.parse::().expect(&front); + if let Some(token) = self.buffer.pop() { + return token.parse().ok().expect("Failed parse"); } let mut input = String::new(); - self.reader.read_line(&mut input).expect("Line not read"); + self.reader.read_line(&mut input).expect("Failed read"); self.buffer = input.split_whitespace().rev().map(String::from).collect(); } } } +impl Scanner> { + pub fn from_file(filename: &str) -> Self { + let file = std::fs::File::open(filename).expect("File not found"); + Self::new(io::BufReader::new(file)) + } +} + #[cfg(test)] mod test { use super::*; @@ -61,8 +65,7 @@ mod test { #[test] #[should_panic(expected = "File not found")] fn test_file() { - let file = ::std::fs::File::open("asdf.txt").expect("File not found"); - let mut scan = Scanner::new(io::BufReader::new(file)); + let mut scan = Scanner::from_file("asdf.txt"); let _ = scan.read::(); } } From fa082496e29139a0ccf276ab50f19743be444387 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 28 May 2019 16:11:20 -0700 Subject: [PATCH 21/71] Rational, Complex, FFT --- .travis.yml | 5 + src/math/fft.rs | 130 ++++++++++++++++++++++++ src/{math.rs => math/mod.rs} | 2 + src/math/num.rs | 192 +++++++++++++++++++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 src/math/fft.rs rename src/{math.rs => math/mod.rs} (98%) create mode 100644 src/math/num.rs diff --git a/.travis.yml b/.travis.yml index c2fe7a1..0084684 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,11 @@ rust: - stable - beta - nightly +before_script: + - rustup component add clippy +script: + - cargo test --verbose + - cargo clippy -- -D warnings matrix: allow_failures: - rust: nightly diff --git a/src/math/fft.rs b/src/math/fft.rs new file mode 100644 index 0000000..d791fd2 --- /dev/null +++ b/src/math/fft.rs @@ -0,0 +1,130 @@ +//! The Fast Fourier Transform (FFT) +use super::num::{Complex, PI}; + +// We can delete this struct once f64::reverse_bits() stabilizes. +struct BitRevIterator { + a: usize, + n: usize, +} +impl BitRevIterator { + fn new(n: usize) -> Self { + assert!(n.is_power_of_two()); + Self { a: 2 * n - 1, n } + } +} +impl Iterator for BitRevIterator { + type Item = usize; + + fn next(&mut self) -> Option { + if self.a == 2 * self.n - 2 { + return None; + } + let mut mask = self.n; + while self.a & mask > 0 { + self.a ^= mask; + mask /= 2; + } + self.a |= mask; + Some(self.a / 2) + } +} + +// Integer FFT notes: see problem 30-6 in CLRS for details, noting that +// 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod p=(15<<27)+1 +// 125 and 2267742733 are inverses and 2^30th roots of 1 mod p=(3<<30)+1 + +/// Computes the discrete fourier transform of v, whose length is a power of 2. +/// Forward transform: polynomial coefficients -> evaluate at roots of unity +/// Inverse transform: values at roots of unity -> interpolated coefficients +pub fn fft(v: &[Complex], inverse: bool) -> Vec { + let n = v.len(); + assert!(n.is_power_of_two()); + + let step = if inverse { -2.0 } else { 2.0 } * PI / n as f64; + let factor = Complex::from(if inverse { n as f64 } else { 1.0 }); + let roots_of_unity = (0..n / 2) + .map(|i| Complex::from_polar(1.0, step * i as f64)) + .collect::>(); + let mut dft = BitRevIterator::new(n) + .map(|i| v[i] / factor) + .collect::>(); + + for m in (0..).map(|s| 1 << s).take_while(|&m| m < n) { + for j in 0..m { + for k in (0..n).step_by(2 * m) { + let u = dft[k + j]; + let t = dft[k + j + m] * roots_of_unity[n / 2 / m * j]; + dft[k + j] = u + t; + dft[k + j + m] = u - t; + } + } + } + dft +} + +/// From a real vector, computes a DFT of size at least desired_len +pub fn dft_from_reals(v: &[f64], desired_len: usize) -> Vec { + assert!(v.len() <= desired_len); + let complex_v = v + .iter() + .cloned() + .chain(std::iter::repeat(0.0)) + .take(desired_len.next_power_of_two()) + .map(Complex::from) + .collect::>(); + fft(&complex_v, false) +} + +/// The inverse of dft_from_reals() +pub fn idft_to_reals(fft_v: &[Complex], desired_len: usize) -> Vec { + assert!(fft_v.len() >= desired_len); + let complex_v = fft(fft_v, true); + complex_v + .into_iter() + .take(desired_len) + .map(|c| c.real) // to get integers: c.real.round() as i64 + .collect() +} + +/// Given two polynomials (vectors) sum_i a[i]x^i and sum_i b[i]x^i, +/// computes their product (convolution) c[k] = sum_(i+j=k) a[i]*b[j] +pub fn convolution(a: &[f64], b: &[f64]) -> Vec { + let len_c = a.len() + b.len() - 1; + let dft_a = dft_from_reals(a, len_c); + let dft_b = dft_from_reals(b, len_c); + let dft_c = dft_a + .into_iter() + .zip(dft_b.into_iter()) + .map(|(a, b)| a * b) + .collect::>(); + idft_to_reals(&dft_c, len_c) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_dft() { + let v = vec![7.0, 1.0, 1.0]; + let dft_v = dft_from_reals(&v, v.len()); + let new_v = idft_to_reals(&dft_v, v.len()); + + let six = Complex::from(6.0); + let seven = Complex::from(7.0); + let nine = Complex::from(9.0); + let i = Complex::new(0.0, 1.0); + + assert_eq!(dft_v, vec![nine, six + i, seven, six - i]); + assert_eq!(new_v, v); + } + + #[test] + fn test_convolution() { + let x = vec![2.0, 3.0, 2.0]; + let y = vec![7.0, 2.0]; + let z = convolution(&x, &y); + + assert_eq!(z, vec![14.0, 25.0, 20.0, 4.0]); + } +} diff --git a/src/math.rs b/src/math/mod.rs similarity index 98% rename from src/math.rs rename to src/math/mod.rs index 6380623..b405469 100644 --- a/src/math.rs +++ b/src/math/mod.rs @@ -1,4 +1,6 @@ //! Number-theoretic utilities for contest problems. +pub mod fft; +pub mod num; /// Modular exponentiation by repeated squaring: returns base^exp % m. /// diff --git a/src/math/num.rs b/src/math/num.rs new file mode 100644 index 0000000..5584278 --- /dev/null +++ b/src/math/num.rs @@ -0,0 +1,192 @@ +//! Rational and Complex numbers, minimally viable for contests. +//! For more optimized and full-featured versions, check out the num crate. +// TODO: Once Rust gets const generics, consider implementing a Mod

struct +use super::extended_gcd; +pub use std::f64::consts::PI; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +/// Represents a fraction reduced to lowest terms +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct Rational { + pub num: i64, + pub den: i64, +} +impl Rational { + pub fn new(a: i64, b: i64) -> Self { + let g = extended_gcd(a, b).0 * b.signum(); + Self { + num: a / g, + den: b / g, + } + } + pub fn abs(self) -> Self { + Self { + num: self.num.abs(), + den: self.den, + } + } +} +impl From for Rational { + fn from(num: i64) -> Self { + Self { num, den: 1 } + } +} +impl Neg for Rational { + type Output = Self; + fn neg(self) -> Self { + Self { + num: -self.num, + den: self.den, + } + } +} +#[allow(clippy::suspicious_arithmetic_impl)] +impl Add for Rational { + type Output = Self; + fn add(self, other: Self) -> Self { + Self::new( + self.num * other.den + self.den * other.num, + self.den * other.den, + ) + } +} +#[allow(clippy::suspicious_arithmetic_impl)] +impl Sub for Rational { + type Output = Self; + fn sub(self, other: Self) -> Self { + Self::new( + self.num * other.den - self.den * other.num, + self.den * other.den, + ) + } +} +impl Mul for Rational { + type Output = Self; + fn mul(self, other: Self) -> Self { + Self::new(self.num * other.num, self.den * other.den) + } +} +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Rational { + type Output = Self; + fn div(self, other: Self) -> Self { + Self::new(self.num * other.den, self.den * other.num) + } +} +impl Ord for Rational { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + (self.num * other.den).cmp(&(self.den * other.num)) + } +} +impl PartialOrd for Rational { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Represents a complex number using floating-point arithmetic +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct Complex { + pub real: f64, + pub imag: f64, +} +impl Complex { + pub fn new(a: f64, b: f64) -> Self { + Self { real: a, imag: b } + } + pub fn from_polar(r: f64, th: f64) -> Self { + Self::new(r * th.cos(), r * th.sin()) + } + pub fn conjugate(self) -> Self { + Self::new(self.real, -self.imag) + } + pub fn abs_square(self) -> f64 { + self.real * self.real + self.imag * self.imag + } + pub fn argument(self) -> f64 { + self.imag.atan2(self.real) + } +} +impl From for Complex { + fn from(real: f64) -> Self { + Self::new(real, 0.0) + } +} +impl Neg for Complex { + type Output = Self; + fn neg(self) -> Self { + Self::new(-self.real, -self.imag) + } +} +impl Add for Complex { + type Output = Self; + fn add(self, other: Self) -> Self { + Self::new(self.real + other.real, self.imag + other.imag) + } +} +impl Sub for Complex { + type Output = Self; + fn sub(self, other: Self) -> Self { + Self::new(self.real - other.real, self.imag - other.imag) + } +} +impl Mul for Complex { + type Output = Self; + fn mul(self, other: Self) -> Self { + let real = self.real * other.real - self.imag * other.imag; + let imag = self.imag * other.real + self.real * other.imag; + Self::new(real, imag) + } +} +impl Div for Complex { + type Output = Self; + fn div(self, other: Self) -> Self { + let real = self.real * other.real + self.imag * other.imag; + let imag = self.imag * other.real - self.real * other.imag; + let denom = other.abs_square(); + Self::new(real / denom, imag / denom) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_rational() { + let three = Rational::from(3); + let six = Rational::from(6); + let three_and_half = three + three / six; + + assert_eq!(three_and_half.num, 7); + assert_eq!(three_and_half.den, 2); + assert_eq!(three_and_half, Rational::new(-35, -10)); + assert!(three_and_half > Rational::from(3)); + assert!(three_and_half < Rational::from(4)); + + let minus_three_and_half = six - three_and_half + three / (-three / six); + let zero = three_and_half + minus_three_and_half; + + assert_eq!(minus_three_and_half.num, -7); + assert_eq!(minus_three_and_half.den, 2); + assert_eq!(three_and_half, -minus_three_and_half); + assert_eq!(zero.num, 0); + assert_eq!(zero.den, 1); + } + + #[test] + fn test_complex() { + let four = Complex::new(4.0, 0.0); + let two_i = Complex::new(0.0, 2.0); + + assert_eq!(four / two_i, -two_i); + assert_eq!(two_i * -two_i, four); + assert_eq!(two_i - two_i, Complex::from(0.0)); + assert_eq!(four.abs_square(), 16.0); + assert_eq!(two_i.abs_square(), 4.0); + assert_eq!((-four).argument(), -PI); + assert_eq!((-two_i).argument(), -PI / 2.0); + assert_eq!(four.argument(), 0.0); + assert_eq!(two_i.argument(), PI / 2.0); + } +} From 7c028d78fe93e78c4095ca6ff25f9da77e5dcc17 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 28 May 2019 16:53:45 -0700 Subject: [PATCH 22/71] Packaging for crates.io --- Cargo.toml | 18 ++++++++++++++---- README.md | 7 ++++--- tests/codeforces343d.rs | 8 ++++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index debb9ee..42e72fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,18 @@ [package] -name = "algorithms" -version = "0.1.0" -authors = ["Aram Ebtekar "] +name = "contest-algorithms" +version = "0.1.1" +authors = ["Aram Ebtekar"] edition = "2018" +description = "Common algorithms and data structures for programming contests" +repository = "https://github.com/EbTech/rust-algorithms" +readme = "README.md" +keywords = ["competitive", "programming", "codeforces"] +categories = ["algorithms", "data-structures"] +license = "MIT" + +[badges] +travis-ci = { repository = "EbTech/rust-algorithms", branch = "master" } + [dependencies] -bit-vec = "*" +bit-vec = "0.6.*" diff --git a/README.md b/README.md index df691fb..eb12919 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# Algorithm Cookbook in Rust +# Contest Algorithms in Rust [![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) +[![Latest Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) A collection of classic data structures and algorithms, emphasizing beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as competition programmers. @@ -14,7 +15,7 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Hackerrank](https://www.hackerrank.com/) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com/). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [Hackerrank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. @@ -36,6 +37,6 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT - [Associative range query](src/arq_tree.rs): known colloquially as *segtrees* -- [Math](src/math.rs): Euclid's GCD algorithm, Bezout's identity +- [Math](src/math/): Euclid's GCD algorithm, Bezout's identity, rational and complex numbers, fast Fourier transform - [Scanner](src/scanner.rs): utility for reading input data - [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 1e0e349..fc7488c 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -2,10 +2,10 @@ //! To make a self-contained file for contest submission, dump each desired //! module's contents directly here instead of the use statements. //! Also, replace io::Cursor with io::stdin as shown in scanner.rs. -extern crate algorithms; -use algorithms::arq_tree::{ArqTree, AssignSum}; -use algorithms::graph::Graph; -use algorithms::scanner::Scanner; +extern crate contest_algorithms; +use contest_algorithms::arq_tree::{ArqTree, AssignSum}; +use contest_algorithms::graph::Graph; +use contest_algorithms::scanner::Scanner; const SAMPLE_INPUT: &str = "\ 5 From a21599de2bb56885ae901ec13e9d68f32d8dd459 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Thu, 30 May 2019 17:35:24 -0700 Subject: [PATCH 23/71] BufWriter example, for the benefit of contest users --- README.md | 2 +- src/math/fft.rs | 17 +++++++---------- src/scanner.rs | 6 +++++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eb12919..3b4c4b7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [Hackerrank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [HackerRank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. diff --git a/src/math/fft.rs b/src/math/fft.rs index d791fd2..e6ce9fb 100644 --- a/src/math/fft.rs +++ b/src/math/fft.rs @@ -30,6 +30,7 @@ impl Iterator for BitRevIterator { } // Integer FFT notes: see problem 30-6 in CLRS for details, noting that +// 15311432 and 469870224 are inverses and 2^23rd roots of 1 mod p=(119<<23)+1 // 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod p=(15<<27)+1 // 125 and 2267742733 are inverses and 2^30th roots of 1 mod p=(3<<30)+1 @@ -76,9 +77,9 @@ pub fn dft_from_reals(v: &[f64], desired_len: usize) -> Vec { } /// The inverse of dft_from_reals() -pub fn idft_to_reals(fft_v: &[Complex], desired_len: usize) -> Vec { - assert!(fft_v.len() >= desired_len); - let complex_v = fft(fft_v, true); +pub fn idft_to_reals(dft_v: &[Complex], desired_len: usize) -> Vec { + assert!(dft_v.len() >= desired_len); + let complex_v = fft(dft_v, true); complex_v .into_iter() .take(desired_len) @@ -90,13 +91,9 @@ pub fn idft_to_reals(fft_v: &[Complex], desired_len: usize) -> Vec { /// computes their product (convolution) c[k] = sum_(i+j=k) a[i]*b[j] pub fn convolution(a: &[f64], b: &[f64]) -> Vec { let len_c = a.len() + b.len() - 1; - let dft_a = dft_from_reals(a, len_c); - let dft_b = dft_from_reals(b, len_c); - let dft_c = dft_a - .into_iter() - .zip(dft_b.into_iter()) - .map(|(a, b)| a * b) - .collect::>(); + let dft_a = dft_from_reals(a, len_c).into_iter(); + let dft_b = dft_from_reals(b, len_c).into_iter(); + let dft_c = dft_a.zip(dft_b).map(|(a, b)| a * b).collect::>(); idft_to_reals(&dft_c, len_c) } diff --git a/src/scanner.rs b/src/scanner.rs index 5ab98fa..3af1e69 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -54,11 +54,15 @@ mod test { } #[test] - fn test_stdin() { + fn test_compile_stdinout() { let stdin = io::stdin(); let mut scan = Scanner::new(stdin.lock()); + use io::Write; + let out = &mut io::BufWriter::new(io::stdout()); + if false { let _ = scan.read::(); + writeln!(out, "Test").ok(); } } From 8148cf2898cfd2a35eee50238c373d6912e9a738 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 31 May 2019 15:20:26 -0700 Subject: [PATCH 24/71] Safe modular arithmetic and number theoretic transform --- .travis.yml | 2 +- README.md | 8 ++- src/math/fft.rs | 169 +++++++++++++++++++++++++++++++++++++++--------- src/math/mod.rs | 32 +-------- src/math/num.rs | 134 ++++++++++++++++++++++++++++++++------ 5 files changed, 263 insertions(+), 82 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0084684..0e7d38d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.31.1 # Version currently supported by Codeforces + # - 1.31.1 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/README.md b/README.md index 3b4c4b7..b24ad0b 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,15 @@ Most competition programmers rely on C++ for its fast execution time. However, i To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! +For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/54859899). + Other online judges that support Rust: - [Timus](http://acm.timus.ru/help.aspx?topic=rust) - [SPOJ](http://www.spoj.com/) ## Programming Language Advocacy -My other goal is to appeal to developers who feel limited by mainstream, arguably outdated, programming languages. Perhaps we have a better way. +My other goal is to appeal to developers who feel limited by mainstream, arguably outdated, programming languages. Perhaps we have a better option. Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/2018-edition) @@ -37,6 +39,8 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT - [Associative range query](src/arq_tree.rs): known colloquially as *segtrees* -- [Math](src/math/): Euclid's GCD algorithm, Bezout's identity, rational and complex numbers, fast Fourier transform +- [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity +- [Arithmetic](src/math/num.rs): rational and complex numbers, safe modular arithmetic +- [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data - [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search diff --git a/src/math/fft.rs b/src/math/fft.rs index e6ce9fb..693dfe1 100644 --- a/src/math/fft.rs +++ b/src/math/fft.rs @@ -1,5 +1,6 @@ //! The Fast Fourier Transform (FFT) -use super::num::{Complex, PI}; +use super::num::{Complex, Field, PI}; +use std::ops::{Add, Div, Mul, Neg, Sub}; // We can delete this struct once f64::reverse_bits() stabilizes. struct BitRevIterator { @@ -29,30 +30,100 @@ impl Iterator for BitRevIterator { } } -// Integer FFT notes: see problem 30-6 in CLRS for details, noting that -// 15311432 and 469870224 are inverses and 2^23rd roots of 1 mod p=(119<<23)+1 -// 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod p=(15<<27)+1 -// 125 and 2267742733 are inverses and 2^30th roots of 1 mod p=(3<<30)+1 +pub trait FFT: Sized + Copy { + type F: Sized + + Copy + + From + + Neg + + Add + + Div + + Mul + + Sub; + + const ZERO: Self; + + /// A primitive nth root of one raised to the powers 0, 1, 2, ..., n/2 - 1 + fn get_roots(n: usize, inverse: bool) -> Vec; + /// 1 for forward transform, 1/n for inverse transform + fn get_factor(n: usize, inverse: bool) -> Self::F; + /// The inverse of Self::F::from() + fn extract(f: Self::F) -> Self; +} + +impl FFT for f64 { + type F = Complex; + + const ZERO: f64 = 0.0; + + fn get_roots(n: usize, inverse: bool) -> Vec { + let step = if inverse { -2.0 } else { 2.0 } * PI / n as f64; + (0..n / 2) + .map(|i| Complex::from_polar(1.0, step * i as f64)) + .collect() + } + + fn get_factor(n: usize, inverse: bool) -> Self::F { + Self::F::from(if inverse { (n as f64).recip() } else { 1.0 }) + } + + fn extract(f: Self::F) -> f64 { + f.real + } +} + +// NTT notes: see problem 30-6 in CLRS for details, keeping in mind that +// 15311432 and 469870224 are inverses and 2^23rd roots of 1 mod (119<<23)+1 +// 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod (15<<27)+1 +// 125 and 2267742733 are inverses and 2^30th roots of 1 mod (3<<30)+1 +impl FFT for u64 { + type F = Field; + + const ZERO: u64 = 0; + + fn get_roots(n: usize, inverse: bool) -> Vec { + assert!(n <= 1 << 23); + let mut prim_root = Self::F::from(15_311_432); + if inverse { + prim_root = prim_root.recip(); + } + for _ in (0..).take_while(|&i| n < 1 << (23 - i)) { + prim_root = prim_root * prim_root; + } + + let mut roots = Vec::with_capacity(n / 2); + let mut root = Self::F::from(1); + for _ in 0..roots.capacity() { + roots.push(root); + root = root * prim_root; + } + roots + } + + fn get_factor(n: usize, inverse: bool) -> Self::F { + Self::F::from(if inverse { n as u64 } else { 1 }).recip() + } + + fn extract(f: Self::F) -> u64 { + f.val + } +} /// Computes the discrete fourier transform of v, whose length is a power of 2. /// Forward transform: polynomial coefficients -> evaluate at roots of unity /// Inverse transform: values at roots of unity -> interpolated coefficients -pub fn fft(v: &[Complex], inverse: bool) -> Vec { +pub fn fft(v: &[T::F], inverse: bool) -> Vec { let n = v.len(); assert!(n.is_power_of_two()); - let step = if inverse { -2.0 } else { 2.0 } * PI / n as f64; - let factor = Complex::from(if inverse { n as f64 } else { 1.0 }); - let roots_of_unity = (0..n / 2) - .map(|i| Complex::from_polar(1.0, step * i as f64)) - .collect::>(); + let factor = T::get_factor(n, inverse); + let roots_of_unity = T::get_roots(n, inverse); let mut dft = BitRevIterator::new(n) - .map(|i| v[i] / factor) + .map(|i| v[i] * factor) .collect::>(); for m in (0..).map(|s| 1 << s).take_while(|&m| m < n) { - for j in 0..m { - for k in (0..n).step_by(2 * m) { + for k in (0..n).step_by(2 * m) { + for j in 0..m { let u = dft[k + j]; let t = dft[k + j + m] * roots_of_unity[n / 2 / m * j]; dft[k + j] = u + t; @@ -63,33 +134,36 @@ pub fn fft(v: &[Complex], inverse: bool) -> Vec { dft } -/// From a real vector, computes a DFT of size at least desired_len -pub fn dft_from_reals(v: &[f64], desired_len: usize) -> Vec { +/// From a slice of reals (f64 or u64), computes DFT of size at least desired_len +pub fn dft_from_reals(v: &[T], desired_len: usize) -> Vec { assert!(v.len() <= desired_len); + let complex_v = v .iter() .cloned() - .chain(std::iter::repeat(0.0)) + .chain(std::iter::repeat(T::ZERO)) .take(desired_len.next_power_of_two()) - .map(Complex::from) + .map(T::F::from) .collect::>(); - fft(&complex_v, false) + fft::(&complex_v, false) } /// The inverse of dft_from_reals() -pub fn idft_to_reals(dft_v: &[Complex], desired_len: usize) -> Vec { +pub fn idft_to_reals(dft_v: &[T::F], desired_len: usize) -> Vec { assert!(dft_v.len() >= desired_len); - let complex_v = fft(dft_v, true); + + let complex_v = fft::(dft_v, true); complex_v .into_iter() .take(desired_len) - .map(|c| c.real) // to get integers: c.real.round() as i64 + .map(T::extract) .collect() } -/// Given two polynomials (vectors) sum_i a[i]x^i and sum_i b[i]x^i, -/// computes their product (convolution) c[k] = sum_(i+j=k) a[i]*b[j] -pub fn convolution(a: &[f64], b: &[f64]) -> Vec { +/// Given two polynomials (vectors) sum_i a[i] x^i and sum_i b[i] x^i, +/// computes their product (convolution) c[k] = sum_(i+j=k) a[i]*b[j]. +/// Uses complex FFT if inputs are f64, or modular NTT if inputs are u64. +pub fn convolution(a: &[T], b: &[T]) -> Vec { let len_c = a.len() + b.len() - 1; let dft_a = dft_from_reals(a, len_c).into_iter(); let dft_b = dft_from_reals(b, len_c).into_iter(); @@ -102,10 +176,10 @@ mod test { use super::*; #[test] - fn test_dft() { + fn test_complex_dft() { let v = vec![7.0, 1.0, 1.0]; let dft_v = dft_from_reals(&v, v.len()); - let new_v = idft_to_reals(&dft_v, v.len()); + let new_v: Vec = idft_to_reals(&dft_v, v.len()); let six = Complex::from(6.0); let seven = Complex::from(7.0); @@ -117,11 +191,44 @@ mod test { } #[test] - fn test_convolution() { - let x = vec![2.0, 3.0, 2.0]; - let y = vec![7.0, 2.0]; + fn test_modular_dft() { + let v = vec![7, 1, 1]; + let dft_v = dft_from_reals(&v, v.len()); + let new_v: Vec = idft_to_reals(&dft_v, v.len()); + + let seven = Field::from(7); + let one = Field::from(1); + let prim = Field::from(15_311_432).pow(1 << 21); + let prim2 = prim * prim; + + let eval0 = seven + one + one; + let eval1 = seven + prim + prim2; + let eval2 = seven + prim2 + one; + let eval3 = seven + prim.recip() + prim2; + + assert_eq!(dft_v, vec![eval0, eval1, eval2, eval3]); + assert_eq!(new_v, v); + } + + #[test] + fn test_complex_convolution() { + let x = vec![7.0, 1.0, 1.0]; + let y = vec![2.0, 4.0]; + let z = convolution(&x, &y); + let m = convolution(&vec![999.0], &vec![1e6]); + + assert_eq!(z, vec![14.0, 30.0, 6.0, 4.0]); + assert_eq!(m, vec![999e6]); + } + + #[test] + fn test_modular_convolution() { + let x = vec![7, 1, 1]; + let y = vec![2, 4]; let z = convolution(&x, &y); + let m = convolution(&vec![999], &vec![1_000_000]); - assert_eq!(z, vec![14.0, 25.0, 20.0, 4.0]); + assert_eq!(z, vec![14, 30, 6, 4]); + assert_eq!(m, vec![999_000_000 - Field::MOD]); } } diff --git a/src/math/mod.rs b/src/math/mod.rs index b405469..d53d33f 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -2,30 +2,13 @@ pub mod fft; pub mod num; -/// Modular exponentiation by repeated squaring: returns base^exp % m. -/// -/// # Panics -/// -/// Panics if m == 0. May panic on overflow if m * m > 2^63. -pub fn mod_pow(mut base: u64, mut exp: u64, m: u64) -> u64 { - let mut result = 1 % m; - while exp > 0 { - if exp % 2 == 1 { - result = (result * base) % m; - } - base = (base * base) % m; - exp /= 2; - } - result -} - /// Finds (d, coef_a, coef_b) such that d = gcd(a, b) = a * coef_a + b * coef_b. pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { if b == 0 { (a.abs(), a.signum(), 0) } else { - let (d, coef_a, coef_b) = extended_gcd(b, a % b); - (d, coef_b, coef_a - coef_b * (a / b)) + let (d, coef_b, coef_a) = extended_gcd(b, a % b); + (d, coef_a, coef_b - coef_a * (a / b)) } } @@ -50,17 +33,6 @@ pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> { mod test { use super::*; - #[test] - fn test_mod_inverse() { - let p = 1_000_000_007; - let base = 31; - - let base_inv = mod_pow(base, p - 2, p); - let identity = (base * base_inv) % p; - - assert_eq!(identity, 1); - } - #[test] fn test_egcd() { let (a, b) = (14, 35); diff --git a/src/math/num.rs b/src/math/num.rs index 5584278..7fb00cd 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -1,10 +1,18 @@ -//! Rational and Complex numbers, minimally viable for contests. -//! For more optimized and full-featured versions, check out the num crate. -// TODO: Once Rust gets const generics, consider implementing a Mod

struct -use super::extended_gcd; +//! Safe modular arithmetic as well as Rational and Complex numbers, +//! implemented minimally for contest use. +//! If you need more features, you might be interested in crates.io/crates/num pub use std::f64::consts::PI; use std::ops::{Add, Div, Mul, Neg, Sub}; +/// Fast iterative version of Euclid's GCD algorithm +pub fn fast_gcd(mut a: i64, mut b: i64) -> i64 { + while b != 0 { + a %= b; + std::mem::swap(&mut a, &mut b); + } + a.abs() +} + /// Represents a fraction reduced to lowest terms #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct Rational { @@ -12,11 +20,11 @@ pub struct Rational { pub den: i64, } impl Rational { - pub fn new(a: i64, b: i64) -> Self { - let g = extended_gcd(a, b).0 * b.signum(); + pub fn new(num: i64, den: i64) -> Self { + let g = fast_gcd(num, den) * den.signum(); Self { - num: a / g, - den: b / g, + num: num / g, + den: den / g, } } pub fn abs(self) -> Self { @@ -25,6 +33,13 @@ impl Rational { den: self.den, } } + pub fn recip(self) -> Self { + let g = self.num.signum(); + Self { + num: self.den / g, + den: self.num / g, + } + } } impl From for Rational { fn from(num: i64) -> Self { @@ -70,7 +85,7 @@ impl Mul for Rational { impl Div for Rational { type Output = Self; fn div(self, other: Self) -> Self { - Self::new(self.num * other.den, self.den * other.num) + self * other.recip() } } impl Ord for Rational { @@ -91,21 +106,25 @@ pub struct Complex { pub imag: f64, } impl Complex { - pub fn new(a: f64, b: f64) -> Self { - Self { real: a, imag: b } + pub fn new(real: f64, imag: f64) -> Self { + Self { real, imag } } pub fn from_polar(r: f64, th: f64) -> Self { Self::new(r * th.cos(), r * th.sin()) } - pub fn conjugate(self) -> Self { - Self::new(self.real, -self.imag) - } pub fn abs_square(self) -> f64 { self.real * self.real + self.imag * self.imag } pub fn argument(self) -> f64 { self.imag.atan2(self.real) } + pub fn conjugate(self) -> Self { + Self::new(self.real, -self.imag) + } + fn recip(self) -> Self { + let denom = self.abs_square(); + Self::new(self.real / denom, -self.imag / denom) + } } impl From for Complex { fn from(real: f64) -> Self { @@ -138,13 +157,81 @@ impl Mul for Complex { Self::new(real, imag) } } +#[allow(clippy::suspicious_arithmetic_impl)] impl Div for Complex { type Output = Self; fn div(self, other: Self) -> Self { - let real = self.real * other.real + self.imag * other.imag; - let imag = self.imag * other.real - self.real * other.imag; - let denom = other.abs_square(); - Self::new(real / denom, imag / denom) + self * other.recip() + } +} + +/// Represents an element of the finite (Galois) field of prime order, given by +/// MOD. Until Rust gets const generics, MOD must be hardcoded, but any prime +/// in [1, 2^32] will work. If MOD is not prime, ring operations are still valid +/// but recip() and division are not. Note that the latter operations are also +/// the slowest, so precompute any inverses that you intend to use frequently. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct Field { + pub val: u64, +} +impl Field { + pub const MOD: u64 = 998_244_353; // 2^23 * 7 * 17 + 1 + + pub fn pow(mut self, mut exp: u64) -> Self { + let mut result = Self::from_small(1); + while exp > 0 { + if exp % 2 == 1 { + result = result * self; + } + self = self * self; + exp /= 2; + } + result + } + pub fn recip(self) -> Self { + self.pow(Self::MOD - 2) + } + fn from_small(s: u64) -> Self { + let val = if s < Self::MOD { s } else { s - Self::MOD }; + Self { val } + } +} +impl From for Field { + fn from(val: u64) -> Self { + Self { + val: val % Self::MOD, + } + } +} +impl Neg for Field { + type Output = Self; + fn neg(self) -> Self { + Self::from_small(Self::MOD - self.val) + } +} +impl Add for Field { + type Output = Self; + fn add(self, other: Self) -> Self { + Self::from_small(self.val + other.val) + } +} +impl Sub for Field { + type Output = Self; + fn sub(self, other: Self) -> Self { + Self::from_small(self.val + Self::MOD - other.val) + } +} +impl Mul for Field { + type Output = Self; + fn mul(self, other: Self) -> Self { + Self::from(self.val * other.val) + } +} +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Field { + type Output = Self; + fn div(self, other: Self) -> Self { + self * other.recip() } } @@ -189,4 +276,15 @@ mod test { assert_eq!(four.argument(), 0.0); assert_eq!(two_i.argument(), PI / 2.0); } + + #[test] + fn test_field() { + let base = Field::from(1234); + let zero = base - base; + let one = base.recip() * base; + + assert_eq!(zero.val, 0); + assert_eq!(one.val, 1); + assert_eq!(one / base * (base * base) - base / one, zero); + } } From 54ecd5cbe4a2cb7ed88115ec4ace654f4f893c80 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 3 Jun 2019 14:27:25 -0700 Subject: [PATCH 25/71] Basic linear algebra --- README.md | 2 +- src/math/num.rs | 156 +++++++++++++++++++++++++++++++++++++++- tests/codeforces343d.rs | 28 ++++---- 3 files changed, 167 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b24ad0b..72af62a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT - [Associative range query](src/arq_tree.rs): known colloquially as *segtrees* - [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity -- [Arithmetic](src/math/num.rs): rational and complex numbers, safe modular arithmetic +- [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data - [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search diff --git a/src/math/num.rs b/src/math/num.rs index 7fb00cd..c5fc217 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -1,8 +1,8 @@ -//! Safe modular arithmetic as well as Rational and Complex numbers, +//! Rational and Complex numbers, safe modular arithmetic, and linear algebra, //! implemented minimally for contest use. //! If you need more features, you might be interested in crates.io/crates/num pub use std::f64::consts::PI; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub}; /// Fast iterative version of Euclid's GCD algorithm pub fn fast_gcd(mut a: i64, mut b: i64) -> i64 { @@ -235,6 +235,133 @@ impl Div for Field { } } +#[derive(Clone, PartialEq, Debug)] +pub struct Matrix { + cols: usize, + inner: Box<[f64]>, +} +impl Matrix { + pub fn zero(rows: usize, cols: usize) -> Self { + let inner = vec![0.0; rows * cols].into_boxed_slice(); + Self { cols, inner } + } + pub fn one(cols: usize) -> Self { + let mut matrix = Self::zero(cols, cols); + for i in 0..cols { + matrix[i][i] = 1.0; + } + matrix + } + pub fn vector(vec: &[f64], as_row: bool) -> Self { + let cols = if as_row { vec.len() } else { 1 }; + let inner = vec.to_vec().into_boxed_slice(); + Self { cols, inner } + } + pub fn pow(&self, mut exp: u64) -> Self { + let mut base = self.clone(); + let mut result = Self::one(self.cols); + while exp > 0 { + if exp % 2 == 1 { + result = &result * &base; + } + base = &base * &base; + exp /= 2; + } + result + } + pub fn rows(&self) -> usize { + self.inner.len() / self.cols + } + pub fn transpose(&self) -> Self { + let mut matrix = Matrix::zero(self.cols, self.rows()); + for i in 0..self.rows() { + for j in 0..self.cols { + matrix[j][i] = self[i][j]; + } + } + matrix + } + pub fn recip(&self) -> Self { + unimplemented!(); + } +} +impl Index for Matrix { + type Output = [f64]; + fn index(&self, row: usize) -> &Self::Output { + let start = self.cols * row; + &self.inner[start..start + self.cols] + } +} +impl IndexMut for Matrix { + fn index_mut(&mut self, row: usize) -> &mut Self::Output { + let start = self.cols * row; + &mut self.inner[start..start + self.cols] + } +} +impl Neg for &Matrix { + type Output = Matrix; + fn neg(self) -> Matrix { + let inner = self.inner.iter().map(|&v| -v).collect(); + Matrix { + cols: self.cols, + inner, + } + } +} +impl Add for &Matrix { + type Output = Matrix; + fn add(self, other: Self) -> Matrix { + let self_iter = self.inner.iter(); + let inner = self_iter + .zip(other.inner.iter()) + .map(|(&u, &v)| u + v) + .collect(); + Matrix { + cols: self.cols, + inner, + } + } +} +impl Sub for &Matrix { + type Output = Matrix; + fn sub(self, other: Self) -> Matrix { + let self_iter = self.inner.iter(); + let inner = self_iter + .zip(other.inner.iter()) + .map(|(&u, &v)| u - v) + .collect(); + Matrix { + cols: self.cols, + inner, + } + } +} +impl Mul for &Matrix { + type Output = Matrix; + fn mul(self, scalar: f64) -> Matrix { + let inner = self.inner.iter().map(|&v| v * scalar).collect(); + Matrix { + cols: self.cols, + inner, + } + } +} +impl Mul for &Matrix { + type Output = Matrix; + fn mul(self, other: Self) -> Matrix { + assert_eq!(self.cols, other.rows()); + let mut matrix = Matrix::zero(self.rows(), other.cols); + for i in 0..self.rows() { + for k in 0..self.cols { + for j in 0..other.cols { + matrix[i][j] += self[i][k] * other[k][j]; + } + } + } + matrix + } +} + #[cfg(test)] mod test { use super::*; @@ -287,4 +414,29 @@ mod test { assert_eq!(one.val, 1); assert_eq!(one / base * (base * base) - base / one, zero); } + + #[test] + fn test_linalg() { + let zero = Matrix::zero(2, 2); + let one = Matrix::one(2); + let rotate_90 = Matrix { + cols: 2, + inner: Box::new([0.0, -1.0, 1.0, 0.0]), + }; + let x_vec = Matrix::vector(&[1.0, 0.0], false); + let y_vec = Matrix::vector(&[0.0, 1.0], false); + let x_dot_x = &x_vec.transpose() * &x_vec; + let x_dot_y = &x_vec.transpose() * &y_vec; + + assert_eq!(x_dot_x, Matrix::one(1)); + assert_eq!(x_dot_x[0][0], 1.0); + assert_eq!(x_dot_y, Matrix::zero(1, 1)); + assert_eq!(x_dot_y[0][0], 0.0); + assert_eq!(&one - &one, zero); + assert_eq!(&one * 0.0, zero); + assert_eq!(&rotate_90 * &rotate_90, -&one); + assert_eq!(&rotate_90 * &x_vec, y_vec); + assert_eq!(&rotate_90 * &y_vec, -&x_vec); + assert_eq!(&rotate_90 * &(&x_vec + &y_vec), &y_vec - &x_vec); + } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index fc7488c..541b2c4 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -59,10 +59,18 @@ fn dfs( r[u] = *time; } -fn main1() { +#[test] +fn main() { + use std::fmt::Write; let cursor = std::io::Cursor::new(SAMPLE_INPUT); let mut scan = Scanner::new(cursor); - let mut out = String::new(); + let out = &mut String::new(); + /* To read/write with stdin/stdout instead: + use std::io::{BufWriter, stdin, stdout, Write}; + let stdin = stdin(); + let mut scan = Scanner::new(stdin.lock()); + let out = &mut BufWriter::new(stdout()); + */ let n = scan.read::(); let mut tree = Graph::new(n, 2 * (n - 1)); @@ -91,22 +99,10 @@ fn main1() { } else if c == 2 { arq.modify(l[v], l[v], &0); } else { - use std::fmt::Write; - writeln!(&mut out, "{}", if sum == len { 1 } else { 0 }).unwrap(); + let ans = if sum == len { 1 } else { 0 }; + writeln!(out, "{}", ans).ok(); } } assert_eq!(out, SAMPLE_OUTPUT); } - -#[test] -fn main() { - // If your contest solution requires a lot of stack space, make sure to - // run it in a custom thread like this. - std::thread::Builder::new() - .stack_size(50_000_000) - .spawn(main1) - .unwrap() - .join() - .unwrap(); -} From a8748b18640ab2503060eb9f94e03182335643ca Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Wed, 5 Jun 2019 11:37:53 -0700 Subject: [PATCH 26/71] Cleaned up Scanner unit tests --- README.md | 2 +- src/scanner.rs | 58 +++++++++++++++++++++++++---------------- tests/codeforces343d.rs | 20 +++++++------- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 72af62a..fecb35a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Other online judges that support Rust: My other goal is to appeal to developers who feel limited by mainstream, arguably outdated, programming languages. Perhaps we have a better option. -Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/2018-edition) +Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/) ## Contents diff --git a/src/scanner.rs b/src/scanner.rs index 3af1e69..6b752a3 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -16,12 +16,12 @@ impl Scanner { } } - /// Use "turbofish" syntax read::() to select data type of next token. + /// Use "turbofish" syntax token::() to select data type of next token. /// /// # Panics /// /// Panics if there's an I/O error or if the token cannot be parsed as T. - pub fn read(&mut self) -> T { + pub fn token(&mut self) -> T { loop { if let Some(token) = self.buffer.pop() { return token.parse().ok().expect("Failed parse"); @@ -33,11 +33,14 @@ impl Scanner { } } -impl Scanner> { - pub fn from_file(filename: &str) -> Self { - let file = std::fs::File::open(filename).expect("File not found"); - Self::new(io::BufReader::new(file)) - } +pub fn scanner_from_file(filename: &str) -> Scanner> { + let file = std::fs::File::open(filename).expect("Input file not found"); + Scanner::new(io::BufReader::new(file)) +} + +pub fn writer_to_file(filename: &str) -> io::BufWriter { + let file = std::fs::File::open(filename).expect("Output file not found"); + io::BufWriter::new(file) } #[cfg(test)] @@ -45,31 +48,42 @@ mod test { use super::*; #[test] - fn test_fake_input() { - let cursor = io::Cursor::new("44 2"); + fn test_in_memory_io() { + let cursor = io::Cursor::new("50 8"); let mut scan = Scanner::new(cursor); - let x = scan.read::(); - let y = scan.read::(); - assert_eq!(x - y, 42); + let mut out = String::new(); + use std::fmt::Write; // needed for writeln!() + + let x = scan.token::(); + let y = scan.token::(); + writeln!(out, "Test {}", x - y).ok(); + + assert_eq!(out, "Test 42\n"); } #[test] - fn test_compile_stdinout() { - let stdin = io::stdin(); + fn test_compile_stdio() { + let (stdin, stdout) = (io::stdin(), io::stdout()); let mut scan = Scanner::new(stdin.lock()); - use io::Write; - let out = &mut io::BufWriter::new(io::stdout()); + let mut out = io::BufWriter::new(stdout.lock()); + use io::Write; // needed for writeln!() if false { - let _ = scan.read::(); - writeln!(out, "Test").ok(); + let x = scan.token::(); + let y = scan.token::(); + writeln!(out, "Test {}", x - y).ok(); } } #[test] - #[should_panic(expected = "File not found")] - fn test_file() { - let mut scan = Scanner::from_file("asdf.txt"); - let _ = scan.read::(); + #[should_panic(expected = "Input file not found")] + fn test_panic_file() { + let mut scan = scanner_from_file("input_file.txt"); + let mut out = writer_to_file("output_file.txt"); + use io::Write; // needed for writeln!() + + let x = scan.token::(); + let y = scan.token::(); + writeln!(out, "Test {}", x - y).ok(); } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 541b2c4..7de42a8 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -64,19 +64,19 @@ fn main() { use std::fmt::Write; let cursor = std::io::Cursor::new(SAMPLE_INPUT); let mut scan = Scanner::new(cursor); - let out = &mut String::new(); + let mut out = String::new(); /* To read/write with stdin/stdout instead: - use std::io::{BufWriter, stdin, stdout, Write}; - let stdin = stdin(); + use std::io::{self, Write}; + let (stdin, stdout) = (io::stdin(), io::stdout()); let mut scan = Scanner::new(stdin.lock()); - let out = &mut BufWriter::new(stdout()); + let mut out = io::BufWriter::new(stdout.lock()); */ - let n = scan.read::(); + let n = scan.token::(); let mut tree = Graph::new(n, 2 * (n - 1)); for _ in 1..n { - let u = scan.read::() - 1; - let v = scan.read::() - 1; + let u = scan.token::() - 1; + let v = scan.token::() - 1; tree.add_undirected_edge(u, v); } @@ -86,10 +86,10 @@ fn main() { dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); - let q = scan.read::(); + let q = scan.token::(); for _ in 0..q { - let c = scan.read::(); - let v = scan.read::() - 1; + let c = scan.token::(); + let v = scan.token::() - 1; let (sum, len) = arq.query(l[v], r[v]); if c == 1 { if sum != len { From afa610b70dc618d969f4d6a1de3e2768efc97c85 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Thu, 6 Jun 2019 15:05:45 -0700 Subject: [PATCH 27/71] v0.1.2: added Unsafe Scanner, requires Rust 1.34 --- .travis.yml | 2 +- Cargo.toml | 2 +- README.md | 13 ++++++----- src/math/fft.rs | 2 +- src/scanner.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e7d38d..64df15c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - # - 1.31.1 # Version currently supported by Codeforces + - 1.35.0 # Clippy fails on 1.34.2 - stable - beta - nightly diff --git a/Cargo.toml b/Cargo.toml index 42e72fd..0ce8130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.1.1" +version = "0.1.2" authors = ["Aram Ebtekar"] edition = "2018" diff --git a/README.md b/README.md index fecb35a..9e476d2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) [![Latest Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) -A collection of classic data structures and algorithms, emphasizing beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as competition programmers. +A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. This repository is distributed under the [MIT License](LICENSE). The license text need not be included in contest submissions, though I would appreciate linking back to this repo for others to find. Enjoy! @@ -15,17 +15,18 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](http://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [HackerRank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](https://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [HackerRank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. -Most competition programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. +Most competitive programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! -For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/54859899). +For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). Other online judges that support Rust: +- [Kattis](https://open.kattis.com/help/rust) - [Timus](http://acm.timus.ru/help.aspx?topic=rust) -- [SPOJ](http://www.spoj.com/) +- [SPOJ](https://www.spoj.com/) ## Programming Language Advocacy @@ -42,5 +43,5 @@ Rather than try to persuade you with words, this repository aims to show by exam - [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution -- [Scanner](src/scanner.rs): utility for reading input data +- [Scanner](src/scanner.rs): utility for reading input data ergonomically - [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search diff --git a/src/math/fft.rs b/src/math/fft.rs index 693dfe1..af45971 100644 --- a/src/math/fft.rs +++ b/src/math/fft.rs @@ -1,4 +1,4 @@ -//! The Fast Fourier Transform (FFT) +//! The Fast Fourier Transform (FFT) and Number Theoretic Transform (NTT) use super::num::{Complex, Field, PI}; use std::ops::{Add, Div, Mul, Neg, Sub}; diff --git a/src/scanner.rs b/src/scanner.rs index 6b752a3..ac40a5a 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -1,15 +1,16 @@ //! Generic utility for reading data from standard input, based on [voxl's //! stdin wrapper](http://codeforces.com/contest/702/submission/19589375). use std::io; +use std::str; /// Reads white-space separated tokens one at a time. -pub struct Scanner { - reader: B, +pub struct Scanner { + reader: R, buffer: Vec, } -impl Scanner { - pub fn new(reader: B) -> Self { +impl Scanner { + pub fn new(reader: R) -> Self { Self { reader, buffer: Vec::new(), @@ -21,7 +22,7 @@ impl Scanner { /// # Panics /// /// Panics if there's an I/O error or if the token cannot be parsed as T. - pub fn token(&mut self) -> T { + pub fn token(&mut self) -> T { loop { if let Some(token) = self.buffer.pop() { return token.parse().ok().expect("Failed parse"); @@ -33,6 +34,42 @@ 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, + buf_iter: str::SplitAsciiWhitespace<'static>, +} + +impl UnsafeScanner { + pub fn new(reader: R) -> Self { + Self { + reader, + buf_str: Vec::new(), + buf_iter: "".split_ascii_whitespace(), + } + } + + /// This function should be marked unsafe, but noone has time for that in a + /// programming contest. Use at your own risk! + pub fn token(&mut self) -> T { + loop { + if let Some(token) = self.buf_iter.next() { + return token.parse().ok().expect("Failed parse"); + } + self.buf_str.clear(); + self.reader + .read_until(b'\n', &mut self.buf_str) + .expect("Failed read"); + self.buf_iter = unsafe { + let slice = str::from_utf8_unchecked(&self.buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + } + } + } +} + pub fn scanner_from_file(filename: &str) -> Scanner> { let file = std::fs::File::open(filename).expect("Input file not found"); Scanner::new(io::BufReader::new(file)) @@ -61,6 +98,20 @@ mod test { assert_eq!(out, "Test 42\n"); } + #[test] + fn test_in_memory_unsafe() { + let cursor = io::Cursor::new("50 8"); + let mut scan = UnsafeScanner::new(cursor); + let mut out = String::new(); + use std::fmt::Write; // needed for writeln!() + + let x = scan.token::(); + let y = scan.token::(); + writeln!(out, "Test {}", x - y).ok(); + + assert_eq!(out, "Test 42\n"); + } + #[test] fn test_compile_stdio() { let (stdin, stdout) = (io::stdin(), io::stdout()); From a3df09525568caa1d4b635f62175ec4550865908 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 7 Jun 2019 11:35:18 -0700 Subject: [PATCH 28/71] Field now works with signed values --- .travis.yml | 2 +- src/math/fft.rs | 15 ++++++++------- src/math/num.rs | 33 +++++++++++++++++---------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 64df15c..41032e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.35.0 # Clippy fails on 1.34.2 + - 1.35.0 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/src/math/fft.rs b/src/math/fft.rs index af45971..f5cfb98 100644 --- a/src/math/fft.rs +++ b/src/math/fft.rs @@ -72,13 +72,14 @@ impl FFT for f64 { } // NTT notes: see problem 30-6 in CLRS for details, keeping in mind that +// 2187 and 410692747 are inverses and 2^26th roots of 1 mod (7<<26)+1 // 15311432 and 469870224 are inverses and 2^23rd roots of 1 mod (119<<23)+1 // 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod (15<<27)+1 // 125 and 2267742733 are inverses and 2^30th roots of 1 mod (3<<30)+1 -impl FFT for u64 { +impl FFT for i64 { type F = Field; - const ZERO: u64 = 0; + const ZERO: Self = 0; fn get_roots(n: usize, inverse: bool) -> Vec { assert!(n <= 1 << 23); @@ -100,10 +101,10 @@ impl FFT for u64 { } fn get_factor(n: usize, inverse: bool) -> Self::F { - Self::F::from(if inverse { n as u64 } else { 1 }).recip() + Self::F::from(if inverse { n as Self } else { 1 }).recip() } - fn extract(f: Self::F) -> u64 { + fn extract(f: Self::F) -> Self { f.val } } @@ -134,7 +135,7 @@ pub fn fft(v: &[T::F], inverse: bool) -> Vec { dft } -/// From a slice of reals (f64 or u64), computes DFT of size at least desired_len +/// From a slice of reals (f64 or i64), computes DFT of size at least desired_len pub fn dft_from_reals(v: &[T], desired_len: usize) -> Vec { assert!(v.len() <= desired_len); @@ -162,7 +163,7 @@ pub fn idft_to_reals(dft_v: &[T::F], desired_len: usize) -> Vec { /// Given two polynomials (vectors) sum_i a[i] x^i and sum_i b[i] x^i, /// computes their product (convolution) c[k] = sum_(i+j=k) a[i]*b[j]. -/// Uses complex FFT if inputs are f64, or modular NTT if inputs are u64. +/// Uses complex FFT if inputs are f64, or modular NTT if inputs are i64. pub fn convolution(a: &[T], b: &[T]) -> Vec { let len_c = a.len() + b.len() - 1; let dft_a = dft_from_reals(a, len_c).into_iter(); @@ -194,7 +195,7 @@ mod test { fn test_modular_dft() { let v = vec![7, 1, 1]; let dft_v = dft_from_reals(&v, v.len()); - let new_v: Vec = idft_to_reals(&dft_v, v.len()); + let new_v: Vec = idft_to_reals(&dft_v, v.len()); let seven = Field::from(7); let one = Field::from(1); diff --git a/src/math/num.rs b/src/math/num.rs index c5fc217..f2697da 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -121,7 +121,7 @@ impl Complex { pub fn conjugate(self) -> Self { Self::new(self.real, -self.imag) } - fn recip(self) -> Self { + pub fn recip(self) -> Self { let denom = self.abs_square(); Self::new(self.real / denom, -self.imag / denom) } @@ -166,16 +166,16 @@ impl Div for Complex { } /// Represents an element of the finite (Galois) field of prime order, given by -/// MOD. Until Rust gets const generics, MOD must be hardcoded, but any prime -/// in [1, 2^32] will work. If MOD is not prime, ring operations are still valid +/// MOD. Until Rust gets const generics, MOD must be hardcoded, but any prime in +/// [1, 2^31.5] will work. If MOD is not prime, ring operations are still valid /// but recip() and division are not. Note that the latter operations are also /// the slowest, so precompute any inverses that you intend to use frequently. #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct Field { - pub val: u64, + pub val: i64, } impl Field { - pub const MOD: u64 = 998_244_353; // 2^23 * 7 * 17 + 1 + pub const MOD: i64 = 998_244_353; // 2^23 * 7 * 17 + 1 pub fn pow(mut self, mut exp: u64) -> Self { let mut result = Self::from_small(1); @@ -189,36 +189,35 @@ impl Field { result } pub fn recip(self) -> Self { - self.pow(Self::MOD - 2) + self.pow(Self::MOD as u64 - 2) } - fn from_small(s: u64) -> Self { - let val = if s < Self::MOD { s } else { s - Self::MOD }; + fn from_small(s: i64) -> Self { + let val = if s < 0 { s + Self::MOD } else { s }; Self { val } } } -impl From for Field { - fn from(val: u64) -> Self { - Self { - val: val % Self::MOD, - } +impl From for Field { + fn from(val: i64) -> Self { + // Self { val: val.rem_euclid(Self::MOD) } + Self::from_small(val % Self::MOD) } } impl Neg for Field { type Output = Self; fn neg(self) -> Self { - Self::from_small(Self::MOD - self.val) + Self::from_small(-self.val) } } impl Add for Field { type Output = Self; fn add(self, other: Self) -> Self { - Self::from_small(self.val + other.val) + Self::from_small(self.val + other.val - Self::MOD) } } impl Sub for Field { type Output = Self; fn sub(self, other: Self) -> Self { - Self::from_small(self.val + Self::MOD - other.val) + Self::from_small(self.val - other.val) } } impl Mul for Field { @@ -409,9 +408,11 @@ mod test { let base = Field::from(1234); let zero = base - base; let one = base.recip() * base; + let two = Field::from(2 - 5 * Field::MOD); assert_eq!(zero.val, 0); assert_eq!(one.val, 1); + assert_eq!(one + one, two); assert_eq!(one / base * (base * base) - base / one, zero); } From 97f73dec180d35cb00ffc744fcbd4b1a230465c4 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 21 Jun 2019 19:21:10 -0700 Subject: [PATCH 29/71] ArqTree eager and binary search examples using improved push/pull --- src/arq_tree.rs | 247 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 171 insertions(+), 76 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index e736c6e..675747f 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -7,9 +7,15 @@ /// /// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f /// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r +/// +/// Future work: ArqTree would lend itself naturally to Rust's ownership system. +/// Initially, we should only have access to the root nodes: +/// if size is a power of two, there is a unique root at index 1. +/// arq.push(i) locks i and acquires access to its children. +/// arq.pull(i) is called when the lock on i is released. pub struct ArqTree { - d: Vec>, - t: Vec, + app: Vec>, + val: Vec, } impl ArqTree @@ -17,74 +23,82 @@ where T::F: Clone, { /// Initializes a static balanced tree on top of the given sequence. - pub fn new(init: Vec) -> Self { - let size = init.len(); - let mut t = (0..size).map(|_| T::identity()).collect::>(); - t.append(&mut { init }); - for i in (0..size).rev() { - t[i] = T::op(&t[i << 1], &t[i << 1 | 1]); - } - Self { - d: vec![None; size], - t, + pub fn new(init_val: Vec) -> Self { + let size = init_val.len(); + let mut val = (0..size).map(|_| T::identity()).collect::>(); + val.append(&mut { init_val }); + let app = vec![None; size]; + + let mut arq = Self { app, val }; + for p in (0..size).rev() { + arq.pull(p); } + arq } fn apply(&mut self, p: usize, f: &T::F) { - self.t[p] = T::apply(f, &self.t[p]); - if p < self.d.len() { - let h = match self.d[p] { + self.val[p] = T::apply(f, &self.val[p]); + if let Some(lazy) = self.app.get_mut(p) { + let h = match *lazy { Some(ref g) => T::compose(f, g), None => f.clone(), }; - self.d[p] = Some(h); + *lazy = Some(h); } } fn push(&mut self, p: usize) { + if let Some(ref f) = self.app[p].take() { + self.apply(p << 1, f); + self.apply(p << 1 | 1, f); + } + } + + fn pull(&mut self, p: usize) { + self.val[p] = T::op(&self.val[p << 1], &self.val[p << 1 | 1]); + } + + fn push_to(&mut self, p: usize) { for s in (1..32).rev() { - let i = p >> s; - if let Some(ref f) = self.d[i].take() { - self.apply(i << 1, f); - self.apply(i << 1 | 1, f); - } + self.push(p >> s); } } - fn pull(&mut self, mut p: usize) { + fn pull_from(&mut self, mut p: usize) { while p > 1 { p >>= 1; - if self.d[p].is_none() { - self.t[p] = T::op(&self.t[p << 1], &self.t[p << 1 | 1]); - } + self.pull(p); } } /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. /// /// # Panics /// /// Panics if l or r is out of range. pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { - l += self.d.len(); - r += self.d.len(); - let (l0, r0) = (l, r); - self.push(l0); - self.push(r0); + l += self.app.len(); + r += self.app.len(); + self.push_to(l); + self.push_to(r); + let (mut l0, mut r0) = (1, 1); while l <= r { if l & 1 == 1 { self.apply(l, f); + l0 = l0.max(l); l += 1; } if r & 1 == 0 { self.apply(r, f); + r0 = r0.max(r); r -= 1; } l >>= 1; r >>= 1; } - self.pull(l0); - self.pull(r0); + self.pull_from(l0); + self.pull_from(r0); } /// Returns the aggregate range query on all entries from l to r, inclusive. @@ -93,18 +107,18 @@ where /// /// Panics if l or r is out of range. pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { - l += self.d.len(); - r += self.d.len(); - self.push(l); - self.push(r); + l += self.app.len(); + r += self.app.len(); + self.push_to(l); + self.push_to(r); let (mut l_agg, mut r_agg) = (T::identity(), T::identity()); while l <= r { if l & 1 == 1 { - l_agg = T::op(&l_agg, &self.t[l]); + l_agg = T::op(&l_agg, &self.val[l]); l += 1; } if r & 1 == 0 { - r_agg = T::op(&self.t[r], &r_agg); + r_agg = T::op(&self.val[r], &r_agg); r -= 1; } l >>= 1; @@ -122,8 +136,11 @@ pub trait ArqSpec { type F; /// Type of monoid elements. type M; + + /// For eager updates, compose() ho be unimplemented!(). For lazy updates: /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) fn compose(f: &Self::F, g: &Self::F) -> Self::F; + /// For eager updates, apply() can assume to act on a leaf. For lazy updates: /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) fn apply(f: &Self::F, a: &Self::M) -> Self::M; /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) @@ -132,67 +149,147 @@ pub trait ArqSpec { fn identity() -> Self::M; } -/// Range Minimum Query, a classic application of associative range query. +/// Range Minimum Query (RMQ), a classic application of ARQ. +/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// query(l, r) finds the minimum value in a[l..=r]. +// // Exercises: try augmenting this struct to find the index of a minimum element -// in a range query, as well as the number of elements that match the minimum. -// Then instead of assigning to a range, try to support the operation of -// incrementing each element in a range by a given offset! +// in a range query, as well as the number of elements equal to the minimum. +// Then instead of overwriting values with a constant assignment a[i] = f, +// try supporting addition: a[i] += f. pub struct AssignMin; - impl ArqSpec for AssignMin { type F = i64; type M = i64; - fn compose(f: &Self::F, _: &Self::F) -> Self::F { - *f + fn compose(&f: &Self::F, _: &Self::F) -> Self::F { + f } - fn apply(f: &Self::F, _: &Self::M) -> Self::M { - *f + fn apply(&f: &Self::F, _: &Self::M) -> Self::M { + f } - fn op(a: &Self::M, b: &Self::M) -> Self::M { - (*a).min(*b) + fn op(&a: &Self::M, &b: &Self::M) -> Self::M { + a.min(b) } fn identity() -> Self::M { Self::M::max_value() } } -/// A slightly more advanced application, where we aim to support range sum -/// queries and range constant assignments. -/// Note that constant assignment f_c(a) = c is not a endomorphism on (i64, +) -/// because f_c(a+b) = c != 2*c = f_c(a) + f_c(b). In intuitive terms, the -/// problem is that the internal nodes of the tree should not simply be set to -/// c, but rather, to c multiplied by the subtree size. So let's augment the -/// monoid type with size information, using the 2D vector (a_i,1) instead of -/// a_i. Now check that f_c((a, s)) = (c*s, s) is indeed an endomorphism on -/// vector addition: f_c((a,s)+(b,t)) = f_c((a+b,s+t)) = (c*(s+t),s+t) -/// = (c*s,s)+(c*t,t) = f_c((a,s)) + f_c((b,t)). +/// An example of binary search on an ArqTree. +/// In this case, we use RMQ to locate the leftmost negative element. +/// To ensure the existence of a valid root note (i == 1) from which to descend, +/// the tree size must be a power of two. +pub fn first_negative(arq: &mut ArqTree) -> i32 { + assert!(arq.app.len().is_power_of_two()); + let mut i = 1; + if arq.val[i] >= 0 { + return -1; + } + while i < arq.app.len() { + arq.push(i); + i <<= 1; + if arq.val[i] >= 0 { + i |= 1; + } + } + let pos = i - arq.app.len(); + pos as i32 +} + +/// Range Sum Query, a slightly trickier classic application of ARQ. +/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// query(l, r) sums all the entries a[l..=r]. /// /// # Panics /// /// Associated functions will panic on overflow. +// +// Note that the apply() operation on raw entries is undefined: while leaf nodes +// should simply be set to f, internal nodes must be set to f * size_of_subtree. +// Thus, our monoid type M should store the pair (entry, size_of_subtree). +// +// In mathematical jargon, we say that constant assignment f(a) = f is not an +// endomorphism on (i64, +) because f(a+b) = f != 2*f = f(a) + f(b). +// On the other hand, f((a, s)) = (f*s, s) is indeed an endomorphism on pairs +// with vector addition: f((a, s) + (b, t)) = f((a+b, s+t)) = (f*(s+t), s+t) +// = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). pub struct AssignSum; - impl ArqSpec for AssignSum { type F = i64; type M = (i64, i64); - fn compose(f: &Self::F, _: &Self::F) -> Self::F { - *f + fn compose(&f: &Self::F, _: &Self::F) -> Self::F { + f } - fn apply(f: &Self::F, a: &Self::M) -> Self::M { - (f * a.1, a.1) + fn apply(&f: &Self::F, &(_, s): &Self::M) -> Self::M { + (f * s, s) } - fn op(a: &Self::M, b: &Self::M) -> Self::M { - (a.0 + b.0, a.1 + b.1) + fn op(&(a, s): &Self::M, &(b, t): &Self::M) -> Self::M { + (a + b, s + t) } fn identity() -> Self::M { (0, 0) } } +/// Supply & Demand, based on https://codeforces.com/gym/102218/problem/F +/// modify(i, i, &(p, o)) increases supply by p and demand by o at time i. +/// query(l, r) computes total supply and demand at times l to r, as well as +// how much of the supply is subsequently met by the demand. +// +// Note that the apply() operation is only correct when applied to leaf nodes. +// Therefore, modify() must only be used in "eager" mode, i.e., with l == r. +// compose() should be unimplemented!() to prevent accidental "lazy" updates. +pub struct SupplyDemand; +impl ArqSpec for SupplyDemand { + type F = (i64, i64); + type M = (i64, i64, i64); // production, orders, sales + fn compose(_: &Self::F, _: &Self::F) -> Self::F { + unimplemented!() + } + fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::M) -> Self::M { + let p = p + p_add; + let o = o + o_add; + (p, o, p.min(o)) + } + fn op((p1, o1, s1): &Self::M, (p2, o2, s2): &Self::M) -> Self::M { + let extra = (p1 - s1).min(o2 - s2); + (p1 + p2, o1 + o2, s1 + s2 + extra) + } + fn identity() -> Self::M { + (0, 0, 0) + } +} + #[cfg(test)] mod test { use super::*; + #[test] + fn test_rmq() { + let mut arq = ArqTree::::new(vec![0; 10]); + + assert_eq!(arq.query(0, 9), 0); + + arq.modify(2, 4, &-5); + arq.modify(5, 7, &-3); + arq.modify(1, 6, &1); + + assert_eq!(arq.query(0, 9), -3); + } + + #[test] + fn test_rmq_binary_search() { + let vec = vec![0, 1, -2, 3, -4, -5, 6, -7]; + let mut arq = ArqTree::::new(vec); + let pos = first_negative(&mut arq); + + arq.modify(2, 7, &0); + let pos_zeros = first_negative(&mut arq); + + assert_eq!(pos, 2); + assert_eq!(pos_zeros, -1); + } + #[test] fn test_range_sum() { let mut arq = ArqTree::::new(vec![(0, 1); 10]); @@ -204,17 +301,15 @@ mod test { assert_eq!(arq.query(0, 9), (23, 10)); } - + #[test] - fn test_rmq() { - let mut arq = ArqTree::::new(vec![0; 10]); + fn test_supply_demand() { + let mut arq = ArqTree::::new(vec![(0, 0, 0); 10]); + + arq.modify(1, 1, &(25, 100)); + arq.modify(3, 3, &(100, 30)); + arq.modify(9, 9, &(0, 20)); - assert_eq!(arq.query(0, 9), 0); - - arq.modify(2, 4, &-5); - arq.modify(5, 7, &-3); - arq.modify(1, 6, &1); - - assert_eq!(arq.query(0, 9), -3); + assert_eq!(arq.query(0, 9), (125, 150, 75)); } } From bcc5800eb4bb12a08fc07b9280faf7e970f004ad Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Wed, 3 Jul 2019 11:02:17 -0700 Subject: [PATCH 30/71] v0.1.3: Made string matching generic, fixed writer_to_file() --- Cargo.toml | 2 +- src/arq_tree.rs | 6 +++--- src/math/num.rs | 4 ++-- src/scanner.rs | 2 +- src/string_proc.rs | 53 ++++++++++++++++++++++++++++++---------------- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ce8130..ecc07f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.1.2" +version = "0.1.3" authors = ["Aram Ebtekar"] edition = "2018" diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 675747f..918945f 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -289,7 +289,7 @@ mod test { assert_eq!(pos, 2); assert_eq!(pos_zeros, -1); } - + #[test] fn test_range_sum() { let mut arq = ArqTree::::new(vec![(0, 1); 10]); @@ -301,11 +301,11 @@ mod test { assert_eq!(arq.query(0, 9), (23, 10)); } - + #[test] fn test_supply_demand() { let mut arq = ArqTree::::new(vec![(0, 0, 0); 10]); - + arq.modify(1, 1, &(25, 100)); arq.modify(3, 3, &(100, 30)); arq.modify(9, 9, &(0, 20)); diff --git a/src/math/num.rs b/src/math/num.rs index f2697da..6b75b9b 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -14,7 +14,7 @@ pub fn fast_gcd(mut a: i64, mut b: i64) -> i64 { } /// Represents a fraction reduced to lowest terms -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] pub struct Rational { pub num: i64, pub den: i64, @@ -170,7 +170,7 @@ impl Div for Complex { /// [1, 2^31.5] will work. If MOD is not prime, ring operations are still valid /// but recip() and division are not. Note that the latter operations are also /// the slowest, so precompute any inverses that you intend to use frequently. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] pub struct Field { pub val: i64, } diff --git a/src/scanner.rs b/src/scanner.rs index ac40a5a..265e7a2 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -76,7 +76,7 @@ pub fn scanner_from_file(filename: &str) -> Scanner } pub fn writer_to_file(filename: &str) -> io::BufWriter { - let file = std::fs::File::open(filename).expect("Output file not found"); + let file = std::fs::File::create(filename).expect("Output file not found"); io::BufWriter::new(file) } diff --git a/src/string_proc.rs b/src/string_proc.rs index 101c722..8db4b47 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -1,29 +1,46 @@ //! String processing algorithms. /// Data structure for Knuth-Morris-Pratt string matching against a pattern. -pub struct Matcher<'a> { +pub struct Matcher<'a, T> { /// The string pattern to search for. - pub pattern: &'a [u8], + pub pattern: &'a [T], /// KMP match failure automaton. fail[i] is the length of the longest /// proper prefix-suffix of pattern[0...i]. pub fail: Vec, } -impl<'a> Matcher<'a> { +impl<'a, T: Eq> Matcher<'a, T> { /// Precomputes the automaton that allows linear-time string matching. /// + /// # Example + /// + /// ``` + /// use contest_algorithms::string_proc::Matcher; + /// let utf8_string = "hello"; + /// + /// let match_from_byte_literal = Matcher::new(b"hello"); + /// + /// let match_from_bytes = Matcher::new(utf8_string.as_bytes()); + /// + /// let vec_char: Vec = utf8_string.chars().collect(); + /// let match_from_chars = Matcher::new(&vec_char); + /// + /// let vec_int = vec![4, -3, 1]; + /// let match_from_ints = Matcher::new(&vec_int); + /// ``` + /// /// # Panics /// /// Panics if pattern is empty. - pub fn new(pattern: &'a [u8]) -> Self { + pub fn new(pattern: &'a [T]) -> Self { let mut fail = Vec::with_capacity(pattern.len()); fail.push(0); let mut len = 0; - for &ch in &pattern[1..] { - while len > 0 && pattern[len] != ch { + for ch in &pattern[1..] { + while len > 0 && pattern[len] != *ch { len = fail[len - 1]; } - if pattern[len] == ch { + if pattern[len] == *ch { len += 1; } fail.push(len); @@ -33,17 +50,17 @@ impl<'a> Matcher<'a> { /// KMP algorithm, sets matches[i] = length of longest prefix of pattern /// matching a suffix of text[0...i]. - pub fn kmp_match(&self, text: &[u8]) -> Vec { + pub fn kmp_match(&self, text: &[T]) -> Vec { let mut matches = Vec::with_capacity(text.len()); let mut len = 0; - for &ch in text { + for ch in text { if len == self.pattern.len() { len = self.fail[len - 1]; } - while len > 0 && self.pattern[len] != ch { + while len > 0 && self.pattern[len] != *ch { len = self.fail[len - 1]; } - if self.pattern[len] == ch { + if self.pattern[len] == *ch { len += 1; } matches.push(len); @@ -151,7 +168,7 @@ impl Trie { node.count += 1; for ch in word { - node = { node }.branches.entry(ch).or_insert_with(Default::default); + node = { node }.branches.entry(ch).or_default(); node.count += 1; } } @@ -177,7 +194,7 @@ impl Trie { /// # Panics /// /// Panics if text is empty. -pub fn palindromes(text: &[u8]) -> Vec { +pub fn palindromes(text: &[T]) -> Vec { let mut pal = Vec::with_capacity(2 * text.len() - 1); // only mutable var! pal.push(1); while pal.len() < pal.capacity() { @@ -210,8 +227,8 @@ mod test { #[test] fn test_kmp() { - let text = "banana".as_bytes(); - let pattern = "ana".as_bytes(); + let text = b"banana"; + let pattern = b"ana"; let matches = Matcher::new(pattern).kmp_match(text); @@ -220,8 +237,8 @@ mod test { #[test] fn test_suffix_array() { - let text1 = "bobocel".as_bytes(); - let text2 = "banana".as_bytes(); + let text1 = b"bobocel"; + let text2 = b"banana"; let sfx1 = SuffixArray::new(text1); let sfx2 = SuffixArray::new(text2); @@ -261,7 +278,7 @@ mod test { #[test] fn test_palindrome() { - let text = "banana".as_bytes(); + let text = b"banana"; let pal_len = palindromes(text); From 5291add9afdf9d3f26d585e608f700aa0162fe65 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 19 Jul 2019 00:54:12 -0700 Subject: [PATCH 31/71] Mo's algorithm and precomputing modular inverses --- README.md | 2 +- src/arq_tree.rs | 142 +++++++++++++++++++++++++++++++++++++++++++----- src/math/num.rs | 22 ++++++++ 3 files changed, 152 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9e476d2..133831e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Basic graph representations](src/graph/mod.rs): adjacency lists, minimum spanning tree, Euler path, disjoint set union - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/arq_tree.rs): known colloquially as *segtrees* +- [Associative range query](src/arq_tree.rs): known colloquially as *segtrees*, and Mo's query square root decomposition - [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 918945f..3f1a663 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -18,10 +18,7 @@ pub struct ArqTree { val: Vec, } -impl ArqTree -where - T::F: Clone, -{ +impl ArqTree { /// Initializes a static balanced tree on top of the given sequence. pub fn new(init_val: Vec) -> Self { let size = init_val.len(); @@ -59,7 +56,8 @@ where } fn push_to(&mut self, p: usize) { - for s in (1..32).rev() { + let one_plus_floor_log_p = (p + 1).next_power_of_two().trailing_zeros(); + for s in (1..one_plus_floor_log_p).rev() { self.push(p >> s); } } @@ -76,11 +74,13 @@ where /// /// # Panics /// - /// Panics if l or r is out of range. + /// Panics if r >= size. Note that l > r is valid, meaning an empty range. pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { l += self.app.len(); r += self.app.len(); - self.push_to(l); + if l < r { + self.push_to(l); + } self.push_to(r); let (mut l0, mut r0) = (1, 1); while l <= r { @@ -105,11 +105,13 @@ where /// /// # Panics /// - /// Panics if l or r is out of range. + /// Panics if r >= size. Note that l > r is valid, meaning an empty range. pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { l += self.app.len(); r += self.app.len(); - self.push_to(l); + if l < r { + self.push_to(l); + } self.push_to(r); let (mut l_agg, mut r_agg) = (T::identity(), T::identity()); while l <= r { @@ -133,7 +135,7 @@ pub trait ArqSpec { // Note that while a Fn(M) -> M may seem like a more natural representation // for an endomorphism, compositions would then have to delegate to each of // their parts. This representation is more efficient. - type F; + type F: Clone; /// Type of monoid elements. type M; @@ -157,7 +159,7 @@ pub trait ArqSpec { // in a range query, as well as the number of elements equal to the minimum. // Then instead of overwriting values with a constant assignment a[i] = f, // try supporting addition: a[i] += f. -pub struct AssignMin; +pub enum AssignMin {} impl ArqSpec for AssignMin { type F = i64; type M = i64; @@ -213,7 +215,7 @@ pub fn first_negative(arq: &mut ArqTree) -> i32 { // On the other hand, f((a, s)) = (f*s, s) is indeed an endomorphism on pairs // with vector addition: f((a, s) + (b, t)) = f((a+b, s+t)) = (f*(s+t), s+t) // = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). -pub struct AssignSum; +pub enum AssignSum {} impl ArqSpec for AssignSum { type F = i64; type M = (i64, i64); @@ -239,7 +241,7 @@ impl ArqSpec for AssignSum { // Note that the apply() operation is only correct when applied to leaf nodes. // Therefore, modify() must only be used in "eager" mode, i.e., with l == r. // compose() should be unimplemented!() to prevent accidental "lazy" updates. -pub struct SupplyDemand; +pub enum SupplyDemand {} impl ArqSpec for SupplyDemand { type F = (i64, i64); type M = (i64, i64, i64); // production, orders, sales @@ -260,6 +262,109 @@ impl ArqSpec for SupplyDemand { } } +/// A generic implementation of Mo's algorithm, aka Query Sqrt Decomposition. +/// It answers q offline queries over intervals in 0..n by shifting the query +/// interval's endpoints by one position at a time. +/// Each endpoint incurs a total cost of at most n * sqrt(q * L_OP * R_OP). +pub trait MoState { + type Q; + type A; + + /// cost ratio L_OP / R_OP between a left endpoint and a right endpoint move + const L_R_RATIO: f64 = 1.0; + + fn query(&self, q: &Self::Q) -> Self::A; + fn insert_left(&mut self, pos: usize); + fn remove_left(&mut self, pos: usize); + + fn insert_right(&mut self, pos: usize) { + self.insert_left(pos); + } + fn remove_right(&mut self, pos: usize) { + self.remove_left(pos); + } + /// After initializing self to a state corresponding to an empty interval, + /// call this function to answer all your queries. + fn process(&mut self, queries: &[(usize, usize, Self::Q)]) -> Vec { + let q = queries.len(); + let mut q_positions: Vec = (0..q).collect(); + if let Some(max_r) = queries.iter().map(|&(_, r, _)| r).max() { + let q_adjusted = q as f64 * Self::L_R_RATIO; + let bucket_width = 1 + max_r / q_adjusted.sqrt() as usize; + q_positions.sort_unstable_by_key(|&i| { + let (l, mut r) = (queries[i].0, queries[i].1); + let bucket = l / bucket_width; + if bucket % 2 != 0 { + r = max_r - r; + } + (bucket, r) + }); + } + + let (mut cur_l, mut cur_r) = (1, 0); + let mut answers = Vec::with_capacity(queries.len()); + for i in q_positions { + let (l, r, ref q) = queries[i]; + while cur_l > l { + cur_l -= 1; + self.insert_left(cur_l); + } + while cur_r < r { + cur_r += 1; + self.insert_right(cur_r); + } + while cur_l < l { + self.remove_left(cur_l); + cur_l += 1; + } + while cur_r > r { + self.remove_right(cur_r); + cur_r -= 1; + } + answers.push((i, self.query(q))); + } + answers.sort_unstable_by_key(|&(i, _)| i); + answers.into_iter().map(|(_, ans)| ans).collect() + } +} + +pub struct DistinctVals { + vals: Vec, + counts: Vec, + distinct: usize, +} +impl DistinctVals { + pub fn new(vals: Vec) -> Self { + let max_val = vals.iter().cloned().max().unwrap_or(0); + Self { + vals, + counts: vec![0; max_val + 1], + distinct: 0, + } + } +} +impl MoState for DistinctVals { + type Q = (); + type A = usize; + fn query(&self, _: &Self::Q) -> Self::A { + self.distinct + } + fn insert_left(&mut self, pos: usize) { + let v = self.vals[pos]; + if self.counts[v] == 0 { + self.distinct += 1; + } + self.counts[v] += 1; + } + fn remove_left(&mut self, pos: usize) { + let v = self.vals[pos]; + self.counts[v] -= 1; + if self.counts[v] == 0 { + self.distinct -= 1; + } + } +} + #[cfg(test)] mod test { use super::*; @@ -300,6 +405,7 @@ mod test { arq.modify(3, 5, &1); assert_eq!(arq.query(0, 9), (23, 10)); + assert_eq!(arq.query(10, 4), (0, 0)); } #[test] @@ -312,4 +418,14 @@ mod test { assert_eq!(arq.query(0, 9), (125, 150, 75)); } + + #[test] + fn test_mos_algorithm() { + let queries = vec![(0, 2, ()), (5, 5, ()), (2, 6, ()), (0, 6, ())]; + let arr = vec![4, 8, 4, 7, 1, 9, 8]; + + let answers = DistinctVals::new(arr).process(&queries); + + assert_eq!(answers, vec![2, 1, 5, 5]); + } } diff --git a/src/math/num.rs b/src/math/num.rs index 6b75b9b..7f8df21 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -177,6 +177,7 @@ pub struct Field { impl Field { pub const MOD: i64 = 998_244_353; // 2^23 * 7 * 17 + 1 + /// Computes self^exp in O(log(exp)) time pub fn pow(mut self, mut exp: u64) -> Self { let mut result = Self::from_small(1); while exp > 0 { @@ -188,9 +189,20 @@ impl Field { } result } + /// Computes inverses of 1 to n in O(n) time + pub fn vec_of_recips(n: i64) -> Vec { + let mut recips = vec![Self::from(0), Self::from(1)]; + for i in 2..=n { + let (md, dv) = (Self::MOD % i, Self::MOD / i); + recips.push(recips[md as usize] * Self::from_small(-dv)); + } + recips + } + /// Computes self^-1 in O(log(Self::MOD)) time pub fn recip(self) -> Self { self.pow(Self::MOD as u64 - 2) } + /// Avoids the % operation but requires -Self::MOD <= x < Self::MOD fn from_small(s: i64) -> Self { let val = if s < 0 { s + Self::MOD } else { s }; Self { val } @@ -416,6 +428,16 @@ mod test { assert_eq!(one / base * (base * base) - base / one, zero); } + #[test] + fn test_vec_of_recips() { + let recips = Field::vec_of_recips(20); + + assert_eq!(recips.len(), 21); + for i in 1..recips.len() { + assert_eq!(recips[i], Field::from(i as i64).recip()); + } + } + #[test] fn test_linalg() { let zero = Matrix::zero(2, 2); From 44736e3432f930c4ac9f21d1d5b28cb4ffedf066 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 21 Jul 2019 19:44:59 -0700 Subject: [PATCH 32/71] DynamicArq: dynamic and persistent range queries --- src/arq_tree.rs | 288 ++++++++++++++++++++++++++++++++++++---- tests/codeforces343d.rs | 4 +- 2 files changed, 263 insertions(+), 29 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 3f1a663..937c9a1 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -13,12 +13,12 @@ /// if size is a power of two, there is a unique root at index 1. /// arq.push(i) locks i and acquires access to its children. /// arq.pull(i) is called when the lock on i is released. -pub struct ArqTree { - app: Vec>, +pub struct StaticArq { val: Vec, + app: Vec>, } -impl ArqTree { +impl StaticArq { /// Initializes a static balanced tree on top of the given sequence. pub fn new(init_val: Vec) -> Self { let size = init_val.len(); @@ -26,7 +26,7 @@ impl ArqTree { val.append(&mut { init_val }); let app = vec![None; size]; - let mut arq = Self { app, val }; + let mut arq = Self { val, app }; for p in (0..size).rev() { arq.pull(p); } @@ -130,6 +130,155 @@ impl ArqTree { } } +pub struct DynamicArqNode { + l: i64, + r: i64, + val: T::M, + app: Option, + down: Option<(usize, usize)>, +} + +// TODO: can this be replaced by a #[derive(Clone)]? +impl Clone for DynamicArqNode { + fn clone(&self) -> Self { + Self { + l: self.l, + r: self.r, + val: T::op(&T::identity(), &self.val), + app: self.app.clone(), + down: self.down, + } + } +} + +impl DynamicArqNode { + pub fn new(l: i64, r: i64, init: &dyn Fn(i64, i64) -> T::M) -> Self { + let val = init(l, r); + Self { + l, + r, + val, + app: None, + down: None, + } + } + + fn apply(&mut self, f: &T::F) { + self.val = T::apply(f, &self.val); + if self.l != self.r { + let h = match self.app { + Some(ref g) => T::compose(f, g), + None => f.clone(), + }; + self.app = Some(h); + } + } +} + +/// A dynamic, and optionally persistent, associate range query data structure. +pub struct DynamicArq { + nodes: Vec>, + is_persistent: bool, + initializer: Box T::M>, +} + +impl DynamicArq { + pub fn new( + l: i64, + r: i64, + is_persistent: bool, + initializer: Box T::M>, + ) -> Self { + let nodes = vec![DynamicArqNode::new(l, r, &initializer)]; + Self { + nodes, + is_persistent, + initializer, + } + } + + pub fn new_with_identity(l: i64, r: i64, is_persistent: bool) -> Self { + let initializer = Box::new(|_, _| T::identity()); + Self::new(l, r, is_persistent, initializer) + } + + fn push(&mut self, p: usize) -> (usize, usize) { + let (lp, rp) = match self.nodes[p].down { + Some(children) => children, + None => { + let l = self.nodes[p].l; + let r = self.nodes[p].r; + let m = (l + r) / 2; + self.nodes + .push(DynamicArqNode::new(l, m, &self.initializer)); + self.nodes + .push(DynamicArqNode::new(m + 1, r, &self.initializer)); + (self.nodes.len() - 2, self.nodes.len() - 1) + } + }; + if let Some(ref f) = self.nodes[p].app.take() { + self.nodes[lp].apply(f); + self.nodes[rp].apply(f); + } + (lp, rp) + } + + fn pull(&mut self, p: usize) { + let (lp, rp) = self.nodes[p].down.unwrap(); + let left_val = &self.nodes[lp].val; + let right_val = &self.nodes[rp].val; + self.nodes[p].val = T::op(left_val, right_val); + } + + fn clone_node(&mut self, p: usize) -> usize { + if self.is_persistent { + let node = self.nodes[p].clone(); + self.nodes.push(node); + self.nodes.len() - 1 + } else { + p + } + } + + /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. + pub fn modify(&mut self, p: usize, l: i64, r: i64, f: &T::F) -> usize { + let cur = &self.nodes[p]; + if r < cur.l || cur.r < l { + p + } else if l <= cur.l && cur.r <= r + /* && self.l == self.r forces eager */ + { + let p_clone = self.clone_node(p); + self.nodes[p_clone].apply(f); + p_clone + } else { + let (lp, rp) = self.push(p); + let p_clone = self.clone_node(p); + let lp_clone = self.modify(lp, l, r, f); + let rp_clone = self.modify(rp, l, r, f); + self.nodes[p_clone].down = Some((lp_clone, rp_clone)); + self.pull(p_clone); + p_clone + } + } + + /// Returns the aggregate range query on all entries from l to r, inclusive. + pub fn query(&mut self, p: usize, l: i64, r: i64) -> T::M { + let cur = &self.nodes[p]; + if r < cur.l || cur.r < l { + T::identity() + } else if l <= cur.l && cur.r <= r { + T::op(&T::identity(), &cur.val) + } else { + let (lp, rp) = self.push(p); + let l_agg = self.query(lp, l, r); + let r_agg = self.query(rp, l, r); + T::op(&l_agg, &r_agg) + } + } +} + pub trait ArqSpec { /// Type of data representing an endomorphism. // Note that while a Fn(M) -> M may seem like a more natural representation @@ -177,27 +326,44 @@ impl ArqSpec for AssignMin { } } -/// An example of binary search on an ArqTree. +/// An example of binary search on the tree of a StaticArq. /// In this case, we use RMQ to locate the leftmost negative element. /// To ensure the existence of a valid root note (i == 1) from which to descend, -/// the tree size must be a power of two. -pub fn first_negative(arq: &mut ArqTree) -> i32 { +/// the tree's size must be a power of two. +pub fn first_negative_static(arq: &mut StaticArq) -> i32 { assert!(arq.app.len().is_power_of_two()); - let mut i = 1; - if arq.val[i] >= 0 { + let mut p = 1; + if arq.val[p] >= 0 { return -1; } - while i < arq.app.len() { - arq.push(i); - i <<= 1; - if arq.val[i] >= 0 { - i |= 1; + while p < arq.app.len() { + arq.push(p); + p <<= 1; + if arq.val[p] >= 0 { + p |= 1; } } - let pos = i - arq.app.len(); + let pos = p - arq.app.len(); pos as i32 } +/// An example of binary search on the tree of a DynamicArq. +/// The tree may have any size, not necessarily a power of two. +pub fn first_negative_dynamic(arq: &mut DynamicArq, p: usize) -> i64 { + if arq.nodes[p].val >= 0 { + -1 + } else if arq.nodes[p].l == arq.nodes[p].r { + arq.nodes[p].l + } else { + let (lp, rp) = arq.push(p); + if arq.nodes[lp].val < 0 { + first_negative_dynamic(arq, lp) + } else { + first_negative_dynamic(arq, rp) + } + } +} + /// Range Sum Query, a slightly trickier classic application of ARQ. /// modify(l, r, &f) sets all entries a[l..=r] to f. /// query(l, r) sums all the entries a[l..=r]. @@ -335,7 +501,7 @@ pub struct DistinctVals { } impl DistinctVals { pub fn new(vals: Vec) -> Self { - let max_val = vals.iter().cloned().max().unwrap_or(0); + let &max_val = vals.iter().max().unwrap_or(&0); Self { vals, counts: vec![0; max_val + 1], @@ -371,7 +537,7 @@ mod test { #[test] fn test_rmq() { - let mut arq = ArqTree::::new(vec![0; 10]); + let mut arq = StaticArq::::new(vec![0; 10]); assert_eq!(arq.query(0, 9), 0); @@ -383,21 +549,38 @@ mod test { } #[test] - fn test_rmq_binary_search() { - let vec = vec![0, 1, -2, 3, -4, -5, 6, -7]; - let mut arq = ArqTree::::new(vec); - let pos = first_negative(&mut arq); + fn test_dynamic_rmq() { + let initializer = Box::new(|_, _| 0); + let mut arq = DynamicArq::::new(0, 9, false, initializer); - arq.modify(2, 7, &0); - let pos_zeros = first_negative(&mut arq); + assert_eq!(arq.query(0, 0, 9), 0); - assert_eq!(pos, 2); - assert_eq!(pos_zeros, -1); + arq.modify(0, 2, 4, &-5); + arq.modify(0, 5, 7, &-3); + arq.modify(0, 1, 6, &1); + + assert_eq!(arq.query(0, 0, 9), -3); + } + + #[test] + fn test_persistent_rmq() { + let initializer = Box::new(|_, _| 0); + let mut arq = DynamicArq::::new(0, 9, true, initializer); + + let mut p = 0; + p = arq.modify(p, 2, 4, &-5); + let snapshot = p; + p = arq.modify(p, 5, 7, &-3); + p = arq.modify(p, 1, 6, &1); + + assert_eq!(arq.query(0, 0, 9), 0); + assert_eq!(arq.query(snapshot, 0, 9), -5); + assert_eq!(arq.query(p, 0, 9), -3); } #[test] fn test_range_sum() { - let mut arq = ArqTree::::new(vec![(0, 1); 10]); + let mut arq = StaticArq::::new(vec![(0, 1); 10]); assert_eq!(arq.query(0, 9), (0, 10)); @@ -408,9 +591,23 @@ mod test { assert_eq!(arq.query(10, 4), (0, 0)); } + #[test] + fn test_dynamic_range_sum() { + let initializer = Box::new(|l, r| (0, 1 + r - l)); + let mut arq = DynamicArq::::new(0, 9, false, initializer); + + assert_eq!(arq.query(0, 0, 9), (0, 10)); + + arq.modify(0, 1, 3, &10); + arq.modify(0, 3, 5, &1); + + assert_eq!(arq.query(0, 0, 9), (23, 10)); + assert_eq!(arq.query(0, 10, 4), (0, 0)); + } + #[test] fn test_supply_demand() { - let mut arq = ArqTree::::new(vec![(0, 0, 0); 10]); + let mut arq = StaticArq::::new(vec![(0, 0, 0); 10]); arq.modify(1, 1, &(25, 100)); arq.modify(3, 3, &(100, 30)); @@ -419,6 +616,43 @@ mod test { assert_eq!(arq.query(0, 9), (125, 150, 75)); } + #[test] + fn test_dynamic_supply_demand() { + let mut arq = DynamicArq::::new_with_identity(0, 9, false); + + arq.modify(0, 1, 1, &(25, 100)); + arq.modify(0, 3, 3, &(100, 30)); + arq.modify(0, 9, 9, &(0, 20)); + + assert_eq!(arq.query(0, 0, 9), (125, 150, 75)); + } + + #[test] + fn test_binary_search_rmq() { + let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; + let mut arq = StaticArq::::new(vec); + let pos = first_negative_static(&mut arq); + + arq.modify(3, 7, &0); + let pos_zeros = first_negative_static(&mut arq); + + assert_eq!(pos, 3); + assert_eq!(pos_zeros, -1); + } + + #[test] + fn test_dynamic_binary_search_rmq() { + let initializer = Box::new(|_, r| 2 - r); + let mut arq = DynamicArq::::new(0, 7, false, initializer); + let pos = first_negative_dynamic(&mut arq, 0); + + arq.modify(0, 2, 7, &0); + let pos_zeros = first_negative_dynamic(&mut arq, 0); + + assert_eq!(pos, 3); + assert_eq!(pos_zeros, -1); + } + #[test] fn test_mos_algorithm() { let queries = vec![(0, 2, ()), (5, 5, ()), (2, 6, ()), (0, 6, ())]; diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 7de42a8..c079c86 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,7 +3,7 @@ //! module's contents directly here instead of the use statements. //! Also, replace io::Cursor with io::stdin as shown in scanner.rs. extern crate contest_algorithms; -use contest_algorithms::arq_tree::{ArqTree, AssignSum}; +use contest_algorithms::arq_tree::{AssignSum, StaticArq}; use contest_algorithms::graph::Graph; use contest_algorithms::scanner::Scanner; @@ -85,7 +85,7 @@ fn main() { let mut p = vec![0; n]; dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); - let mut arq = ArqTree::::new(vec![(0, 1); n + 1]); + let mut arq = StaticArq::::new(vec![(0, 1); n + 1]); let q = scan.token::(); for _ in 0..q { let c = scan.token::(); From a713edbdc07d4130c9c3dd1dd4a4788a248a8f0f Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 21 Jul 2019 23:02:32 -0700 Subject: [PATCH 33/71] more memory-efficient DynamicArqNode --- src/arq_tree.rs | 114 +++++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 937c9a1..9847f58 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -131,8 +131,6 @@ impl StaticArq { } pub struct DynamicArqNode { - l: i64, - r: i64, val: T::M, app: Option, down: Option<(usize, usize)>, @@ -142,8 +140,6 @@ pub struct DynamicArqNode { impl Clone for DynamicArqNode { fn clone(&self) -> Self { Self { - l: self.l, - r: self.r, val: T::op(&T::identity(), &self.val), app: self.app.clone(), down: self.down, @@ -152,20 +148,17 @@ impl Clone for DynamicArqNode { } impl DynamicArqNode { - pub fn new(l: i64, r: i64, init: &dyn Fn(i64, i64) -> T::M) -> Self { - let val = init(l, r); + pub fn new(val: T::M) -> Self { Self { - l, - r, val, app: None, down: None, } } - fn apply(&mut self, f: &T::F) { + fn apply(&mut self, f: &T::F, is_leaf: bool) { self.val = T::apply(f, &self.val); - if self.l != self.r { + if !is_leaf { let h = match self.app { Some(ref g) => T::compose(f, g), None => f.clone(), @@ -177,6 +170,8 @@ impl DynamicArqNode { /// A dynamic, and optionally persistent, associate range query data structure. pub struct DynamicArq { + l_bound: i64, + r_bound: i64, nodes: Vec>, is_persistent: bool, initializer: Box T::M>, @@ -184,41 +179,42 @@ pub struct DynamicArq { impl DynamicArq { pub fn new( - l: i64, - r: i64, + l_bound: i64, + r_bound: i64, is_persistent: bool, initializer: Box T::M>, ) -> Self { - let nodes = vec![DynamicArqNode::new(l, r, &initializer)]; + let val = initializer(l_bound, r_bound); + let nodes = vec![DynamicArqNode::new(val)]; Self { + l_bound, + r_bound, nodes, is_persistent, initializer, } } - pub fn new_with_identity(l: i64, r: i64, is_persistent: bool) -> Self { + pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> Self { let initializer = Box::new(|_, _| T::identity()); - Self::new(l, r, is_persistent, initializer) + Self::new(l_bound, r_bound, is_persistent, initializer) } - fn push(&mut self, p: usize) -> (usize, usize) { + fn push(&mut self, p: usize, l: i64, r: i64) -> (usize, usize) { + let m = (l + r) / 2; let (lp, rp) = match self.nodes[p].down { Some(children) => children, None => { - let l = self.nodes[p].l; - let r = self.nodes[p].r; - let m = (l + r) / 2; - self.nodes - .push(DynamicArqNode::new(l, m, &self.initializer)); - self.nodes - .push(DynamicArqNode::new(m + 1, r, &self.initializer)); + let l_val = (self.initializer)(l, m); + let r_val = (self.initializer)(m + 1, r); + self.nodes.push(DynamicArqNode::new(l_val)); + self.nodes.push(DynamicArqNode::new(r_val)); (self.nodes.len() - 2, self.nodes.len() - 1) } }; if let Some(ref f) = self.nodes[p].app.take() { - self.nodes[lp].apply(f); - self.nodes[rp].apply(f); + self.nodes[lp].apply(f, l == m); + self.nodes[rp].apply(f, m + 1 == r); } (lp, rp) } @@ -240,43 +236,49 @@ impl DynamicArq { } } - /// Applies the endomorphism f to all entries from l to r, inclusive. - /// If l == r, the updates are eager. Otherwise, they are lazy. - pub fn modify(&mut self, p: usize, l: i64, r: i64, f: &T::F) -> usize { - let cur = &self.nodes[p]; - if r < cur.l || cur.r < l { + fn m_helper(&mut self, p: usize, l: i64, r: i64, f: &T::F, cl: i64, cr: i64) -> usize { + if r < cl || cr < l { p - } else if l <= cur.l && cur.r <= r - /* && self.l == self.r forces eager */ - { + } else if l <= cl && cr <= r /* && self.l == self.r forces eager */ { let p_clone = self.clone_node(p); - self.nodes[p_clone].apply(f); + self.nodes[p_clone].apply(f, l == r); p_clone } else { - let (lp, rp) = self.push(p); + let (lp, rp) = self.push(p, cl, cr); + let cm = (cl + cr) / 2; let p_clone = self.clone_node(p); - let lp_clone = self.modify(lp, l, r, f); - let rp_clone = self.modify(rp, l, r, f); + let lp_clone = self.m_helper(lp, l, r, f, cl, cm); + let rp_clone = self.m_helper(rp, l, r, f, cm + 1, cr); self.nodes[p_clone].down = Some((lp_clone, rp_clone)); self.pull(p_clone); p_clone } } - /// Returns the aggregate range query on all entries from l to r, inclusive. - pub fn query(&mut self, p: usize, l: i64, r: i64) -> T::M { - let cur = &self.nodes[p]; - if r < cur.l || cur.r < l { + fn q_helper(&mut self, p: usize, l: i64, r: i64, cl: i64, cr: i64) -> T::M { + if r < cl || cr < l { T::identity() - } else if l <= cur.l && cur.r <= r { - T::op(&T::identity(), &cur.val) + } else if l <= cl && cr <= r { + T::op(&T::identity(), &self.nodes[p].val) } else { - let (lp, rp) = self.push(p); - let l_agg = self.query(lp, l, r); - let r_agg = self.query(rp, l, r); + let (lp, rp) = self.push(p, cl, cr); + let cm = (cl + cr) / 2; + let l_agg = self.q_helper(lp, l, r, cl, cm); + let r_agg = self.q_helper(rp, l, r, cm + 1, cr); T::op(&l_agg, &r_agg) } } + + /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. + pub fn modify(&mut self, p: usize, l: i64, r: i64, f: &T::F) -> usize { + self.m_helper(p, l, r, f, self.l_bound, self.r_bound) + } + + /// Returns the aggregate range query on all entries from l to r, inclusive. + pub fn query(&mut self, p: usize, l: i64, r: i64) -> T::M { + self.q_helper(p, l, r, self.l_bound, self.r_bound) + } } pub trait ArqSpec { @@ -349,17 +351,18 @@ pub fn first_negative_static(arq: &mut StaticArq) -> i32 { /// An example of binary search on the tree of a DynamicArq. /// The tree may have any size, not necessarily a power of two. -pub fn first_negative_dynamic(arq: &mut DynamicArq, p: usize) -> i64 { +pub fn first_negative_dynamic(arq: &mut DynamicArq, p: usize, cl: i64, cr: i64) -> i64 { if arq.nodes[p].val >= 0 { -1 - } else if arq.nodes[p].l == arq.nodes[p].r { - arq.nodes[p].l + } else if cl == cr { + cl } else { - let (lp, rp) = arq.push(p); + let (lp, rp) = arq.push(p, cl, cr); + let cm = (cl + cr) / 2; if arq.nodes[lp].val < 0 { - first_negative_dynamic(arq, lp) + first_negative_dynamic(arq, lp, cl, cm) } else { - first_negative_dynamic(arq, rp) + first_negative_dynamic(arq, rp, cm + 1, cr) } } } @@ -643,11 +646,12 @@ mod test { #[test] fn test_dynamic_binary_search_rmq() { let initializer = Box::new(|_, r| 2 - r); - let mut arq = DynamicArq::::new(0, 7, false, initializer); - let pos = first_negative_dynamic(&mut arq, 0); + let (l_bound, r_bound) = (0, 7); + let mut arq = DynamicArq::::new(l_bound, r_bound, false, initializer); + let pos = first_negative_dynamic(&mut arq, 0, l_bound, r_bound); arq.modify(0, 2, 7, &0); - let pos_zeros = first_negative_dynamic(&mut arq, 0); + let pos_zeros = first_negative_dynamic(&mut arq, 0, l_bound, r_bound); assert_eq!(pos, 3); assert_eq!(pos_zeros, -1); From eddc8b4fd1d90affa1d0a13dec456c8769ca14be Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 22 Jul 2019 11:57:55 -0700 Subject: [PATCH 34/71] added ArqView for ergonomics --- src/arq_tree.rs | 134 +++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/arq_tree.rs b/src/arq_tree.rs index 9847f58..ff3fe71 100644 --- a/src/arq_tree.rs +++ b/src/arq_tree.rs @@ -168,10 +168,10 @@ impl DynamicArqNode { } } +type ArqView = (usize, i64, i64); + /// A dynamic, and optionally persistent, associate range query data structure. pub struct DynamicArq { - l_bound: i64, - r_bound: i64, nodes: Vec>, is_persistent: bool, initializer: Box T::M>, @@ -183,24 +183,24 @@ impl DynamicArq { r_bound: i64, is_persistent: bool, initializer: Box T::M>, - ) -> Self { + ) -> (Self, ArqView) { let val = initializer(l_bound, r_bound); let nodes = vec![DynamicArqNode::new(val)]; - Self { - l_bound, - r_bound, + let arq = Self { nodes, is_persistent, initializer, - } + }; + let root_view = (0, l_bound, r_bound); + (arq, root_view) } - pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> Self { + pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> (Self, ArqView) { let initializer = Box::new(|_, _| T::identity()); Self::new(l_bound, r_bound, is_persistent, initializer) } - fn push(&mut self, p: usize, l: i64, r: i64) -> (usize, usize) { + fn push(&mut self, (p, l, r): ArqView) -> (ArqView, ArqView) { let m = (l + r) / 2; let (lp, rp) = match self.nodes[p].down { Some(children) => children, @@ -216,7 +216,7 @@ impl DynamicArq { self.nodes[lp].apply(f, l == m); self.nodes[rp].apply(f, m + 1 == r); } - (lp, rp) + ((lp, l, m), (rp, m + 1, r)) } fn pull(&mut self, p: usize) { @@ -236,49 +236,41 @@ impl DynamicArq { } } - fn m_helper(&mut self, p: usize, l: i64, r: i64, f: &T::F, cl: i64, cr: i64) -> usize { + /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. + pub fn modify(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { + let (p, cl, cr) = view; if r < cl || cr < l { - p + view } else if l <= cl && cr <= r /* && self.l == self.r forces eager */ { let p_clone = self.clone_node(p); self.nodes[p_clone].apply(f, l == r); - p_clone + (p_clone, cl, cr) } else { - let (lp, rp) = self.push(p, cl, cr); - let cm = (cl + cr) / 2; + let (l_view, r_view) = self.push(view); let p_clone = self.clone_node(p); - let lp_clone = self.m_helper(lp, l, r, f, cl, cm); - let rp_clone = self.m_helper(rp, l, r, f, cm + 1, cr); + let lp_clone = self.modify(l_view, l, r, f).0; + let rp_clone = self.modify(r_view, l, r, f).0; self.nodes[p_clone].down = Some((lp_clone, rp_clone)); self.pull(p_clone); - p_clone + (p_clone, cl, cr) } } - fn q_helper(&mut self, p: usize, l: i64, r: i64, cl: i64, cr: i64) -> T::M { + /// Returns the aggregate range query on all entries from l to r, inclusive. + pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::M { + let (p, cl, cr) = view; if r < cl || cr < l { T::identity() } else if l <= cl && cr <= r { T::op(&T::identity(), &self.nodes[p].val) } else { - let (lp, rp) = self.push(p, cl, cr); - let cm = (cl + cr) / 2; - let l_agg = self.q_helper(lp, l, r, cl, cm); - let r_agg = self.q_helper(rp, l, r, cm + 1, cr); + let (l_view, r_view) = self.push(view); + let l_agg = self.query(l_view, l, r); + let r_agg = self.query(r_view, l, r); T::op(&l_agg, &r_agg) } } - - /// Applies the endomorphism f to all entries from l to r, inclusive. - /// If l == r, the updates are eager. Otherwise, they are lazy. - pub fn modify(&mut self, p: usize, l: i64, r: i64, f: &T::F) -> usize { - self.m_helper(p, l, r, f, self.l_bound, self.r_bound) - } - - /// Returns the aggregate range query on all entries from l to r, inclusive. - pub fn query(&mut self, p: usize, l: i64, r: i64) -> T::M { - self.q_helper(p, l, r, self.l_bound, self.r_bound) - } } pub trait ArqSpec { @@ -351,18 +343,19 @@ pub fn first_negative_static(arq: &mut StaticArq) -> i32 { /// An example of binary search on the tree of a DynamicArq. /// The tree may have any size, not necessarily a power of two. -pub fn first_negative_dynamic(arq: &mut DynamicArq, p: usize, cl: i64, cr: i64) -> i64 { +pub fn first_negative_dynamic(arq: &mut DynamicArq, view: ArqView) -> i64 { + let (p, cl, cr) = view; if arq.nodes[p].val >= 0 { -1 } else if cl == cr { cl } else { - let (lp, rp) = arq.push(p, cl, cr); - let cm = (cl + cr) / 2; + let (l_view, r_view) = arq.push(view); + let lp = l_view.0; if arq.nodes[lp].val < 0 { - first_negative_dynamic(arq, lp, cl, cm) + first_negative_dynamic(arq, l_view) } else { - first_negative_dynamic(arq, rp, cm + 1, cr) + first_negative_dynamic(arq, r_view) } } } @@ -554,31 +547,31 @@ mod test { #[test] fn test_dynamic_rmq() { let initializer = Box::new(|_, _| 0); - let mut arq = DynamicArq::::new(0, 9, false, initializer); + let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); - assert_eq!(arq.query(0, 0, 9), 0); + assert_eq!(arq.query(view, 0, 9), 0); - arq.modify(0, 2, 4, &-5); - arq.modify(0, 5, 7, &-3); - arq.modify(0, 1, 6, &1); + arq.modify(view, 2, 4, &-5); + arq.modify(view, 5, 7, &-3); + arq.modify(view, 1, 6, &1); - assert_eq!(arq.query(0, 0, 9), -3); + assert_eq!(arq.query(view, 0, 9), -3); } #[test] fn test_persistent_rmq() { let initializer = Box::new(|_, _| 0); - let mut arq = DynamicArq::::new(0, 9, true, initializer); + let (mut arq, mut view) = DynamicArq::::new(0, 9, true, initializer); - let mut p = 0; - p = arq.modify(p, 2, 4, &-5); - let snapshot = p; - p = arq.modify(p, 5, 7, &-3); - p = arq.modify(p, 1, 6, &1); + let at_init = view; + view = arq.modify(view, 2, 4, &-5); + let snapshot = view; + view = arq.modify(view, 5, 7, &-3); + view = arq.modify(view, 1, 6, &1); - assert_eq!(arq.query(0, 0, 9), 0); + assert_eq!(arq.query(at_init, 0, 9), 0); assert_eq!(arq.query(snapshot, 0, 9), -5); - assert_eq!(arq.query(p, 0, 9), -3); + assert_eq!(arq.query(view, 0, 9), -3); } #[test] @@ -597,15 +590,15 @@ mod test { #[test] fn test_dynamic_range_sum() { let initializer = Box::new(|l, r| (0, 1 + r - l)); - let mut arq = DynamicArq::::new(0, 9, false, initializer); + let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); - assert_eq!(arq.query(0, 0, 9), (0, 10)); + assert_eq!(arq.query(view, 0, 9), (0, 10)); - arq.modify(0, 1, 3, &10); - arq.modify(0, 3, 5, &1); + arq.modify(view, 1, 3, &10); + arq.modify(view, 3, 5, &1); - assert_eq!(arq.query(0, 0, 9), (23, 10)); - assert_eq!(arq.query(0, 10, 4), (0, 0)); + assert_eq!(arq.query(view, 0, 9), (23, 10)); + assert_eq!(arq.query(view, 10, 4), (0, 0)); } #[test] @@ -621,13 +614,13 @@ mod test { #[test] fn test_dynamic_supply_demand() { - let mut arq = DynamicArq::::new_with_identity(0, 9, false); + let (mut arq, view) = DynamicArq::::new_with_identity(0, 9, false); - arq.modify(0, 1, 1, &(25, 100)); - arq.modify(0, 3, 3, &(100, 30)); - arq.modify(0, 9, 9, &(0, 20)); + arq.modify(view, 1, 1, &(25, 100)); + arq.modify(view, 3, 3, &(100, 30)); + arq.modify(view, 9, 9, &(0, 20)); - assert_eq!(arq.query(0, 0, 9), (125, 150, 75)); + assert_eq!(arq.query(view, 0, 9), (125, 150, 75)); } #[test] @@ -646,14 +639,17 @@ mod test { #[test] fn test_dynamic_binary_search_rmq() { let initializer = Box::new(|_, r| 2 - r); - let (l_bound, r_bound) = (0, 7); - let mut arq = DynamicArq::::new(l_bound, r_bound, false, initializer); - let pos = first_negative_dynamic(&mut arq, 0, l_bound, r_bound); + let (l_bound, r_bound) = (0, 1_000_000_000_000_000_000); + let (mut arq, view) = DynamicArq::::new(l_bound, r_bound, false, initializer); + let pos = first_negative_dynamic(&mut arq, view); - arq.modify(0, 2, 7, &0); - let pos_zeros = first_negative_dynamic(&mut arq, 0, l_bound, r_bound); + arq.modify(view, 2, r_bound - 1, &0); + let pos_mostly_zeros = first_negative_dynamic(&mut arq, view); + arq.modify(view, 2, r_bound, &0); + let pos_zeros = first_negative_dynamic(&mut arq, view); assert_eq!(pos, 3); + assert_eq!(pos_mostly_zeros, r_bound); assert_eq!(pos_zeros, -1); } From 1fc577d65bc0dd979d1b1790bc0d1ec8fcf3d9ca Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 22 Jul 2019 22:42:38 -0700 Subject: [PATCH 35/71] Split arq_tree.rs into new range_query directory --- README.md | 2 +- src/arq_tree.rs | 665 --------------------------------- src/graph/flow.rs | 40 +- src/lib.rs | 2 +- src/range_query/dynamic_arq.rs | 161 ++++++++ src/range_query/mod.rs | 135 +++++++ src/range_query/specs.rs | 112 ++++++ src/range_query/sqrt_decomp.rs | 118 ++++++ src/range_query/static_arq.rs | 153 ++++++++ tests/codeforces343d.rs | 2 +- 10 files changed, 702 insertions(+), 688 deletions(-) delete mode 100644 src/arq_tree.rs create mode 100644 src/range_query/dynamic_arq.rs create mode 100644 src/range_query/mod.rs create mode 100644 src/range_query/specs.rs create mode 100644 src/range_query/sqrt_decomp.rs create mode 100644 src/range_query/static_arq.rs diff --git a/README.md b/README.md index 133831e..4b79873 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Basic graph representations](src/graph/mod.rs): adjacency lists, minimum spanning tree, Euler path, disjoint set union - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/arq_tree.rs): known colloquially as *segtrees*, and Mo's query square root decomposition +- [Associative range query](src/range_query): known colloquially as *segtrees*, and Mo's query square root decomposition - [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution diff --git a/src/arq_tree.rs b/src/arq_tree.rs deleted file mode 100644 index ff3fe71..0000000 --- a/src/arq_tree.rs +++ /dev/null @@ -1,665 +0,0 @@ -//! Associative Range Query Tree based on [Al.Cash's compact representation] -//! (http://codeforces.com/blog/entry/18051). - -/// Colloquially known as a "segtree" in the sport programming literature, it -/// represents a sequence of elements a_i (0 <= i < size) from a monoid (M, +) -/// on which we want to support fast range operations: -/// -/// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f -/// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r -/// -/// Future work: ArqTree would lend itself naturally to Rust's ownership system. -/// Initially, we should only have access to the root nodes: -/// if size is a power of two, there is a unique root at index 1. -/// arq.push(i) locks i and acquires access to its children. -/// arq.pull(i) is called when the lock on i is released. -pub struct StaticArq { - val: Vec, - app: Vec>, -} - -impl StaticArq { - /// Initializes a static balanced tree on top of the given sequence. - pub fn new(init_val: Vec) -> Self { - let size = init_val.len(); - let mut val = (0..size).map(|_| T::identity()).collect::>(); - val.append(&mut { init_val }); - let app = vec![None; size]; - - let mut arq = Self { val, app }; - for p in (0..size).rev() { - arq.pull(p); - } - arq - } - - fn apply(&mut self, p: usize, f: &T::F) { - self.val[p] = T::apply(f, &self.val[p]); - if let Some(lazy) = self.app.get_mut(p) { - let h = match *lazy { - Some(ref g) => T::compose(f, g), - None => f.clone(), - }; - *lazy = Some(h); - } - } - - fn push(&mut self, p: usize) { - if let Some(ref f) = self.app[p].take() { - self.apply(p << 1, f); - self.apply(p << 1 | 1, f); - } - } - - fn pull(&mut self, p: usize) { - self.val[p] = T::op(&self.val[p << 1], &self.val[p << 1 | 1]); - } - - fn push_to(&mut self, p: usize) { - let one_plus_floor_log_p = (p + 1).next_power_of_two().trailing_zeros(); - for s in (1..one_plus_floor_log_p).rev() { - self.push(p >> s); - } - } - - fn pull_from(&mut self, mut p: usize) { - while p > 1 { - p >>= 1; - self.pull(p); - } - } - - /// Applies the endomorphism f to all entries from l to r, inclusive. - /// If l == r, the updates are eager. Otherwise, they are lazy. - /// - /// # Panics - /// - /// Panics if r >= size. Note that l > r is valid, meaning an empty range. - pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { - l += self.app.len(); - r += self.app.len(); - if l < r { - self.push_to(l); - } - self.push_to(r); - let (mut l0, mut r0) = (1, 1); - while l <= r { - if l & 1 == 1 { - self.apply(l, f); - l0 = l0.max(l); - l += 1; - } - if r & 1 == 0 { - self.apply(r, f); - r0 = r0.max(r); - r -= 1; - } - l >>= 1; - r >>= 1; - } - self.pull_from(l0); - self.pull_from(r0); - } - - /// Returns the aggregate range query on all entries from l to r, inclusive. - /// - /// # Panics - /// - /// Panics if r >= size. Note that l > r is valid, meaning an empty range. - pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { - l += self.app.len(); - r += self.app.len(); - if l < r { - self.push_to(l); - } - self.push_to(r); - let (mut l_agg, mut r_agg) = (T::identity(), T::identity()); - while l <= r { - if l & 1 == 1 { - l_agg = T::op(&l_agg, &self.val[l]); - l += 1; - } - if r & 1 == 0 { - r_agg = T::op(&self.val[r], &r_agg); - r -= 1; - } - l >>= 1; - r >>= 1; - } - T::op(&l_agg, &r_agg) - } -} - -pub struct DynamicArqNode { - val: T::M, - app: Option, - down: Option<(usize, usize)>, -} - -// TODO: can this be replaced by a #[derive(Clone)]? -impl Clone for DynamicArqNode { - fn clone(&self) -> Self { - Self { - val: T::op(&T::identity(), &self.val), - app: self.app.clone(), - down: self.down, - } - } -} - -impl DynamicArqNode { - pub fn new(val: T::M) -> Self { - Self { - val, - app: None, - down: None, - } - } - - fn apply(&mut self, f: &T::F, is_leaf: bool) { - self.val = T::apply(f, &self.val); - if !is_leaf { - let h = match self.app { - Some(ref g) => T::compose(f, g), - None => f.clone(), - }; - self.app = Some(h); - } - } -} - -type ArqView = (usize, i64, i64); - -/// A dynamic, and optionally persistent, associate range query data structure. -pub struct DynamicArq { - nodes: Vec>, - is_persistent: bool, - initializer: Box T::M>, -} - -impl DynamicArq { - pub fn new( - l_bound: i64, - r_bound: i64, - is_persistent: bool, - initializer: Box T::M>, - ) -> (Self, ArqView) { - let val = initializer(l_bound, r_bound); - let nodes = vec![DynamicArqNode::new(val)]; - let arq = Self { - nodes, - is_persistent, - initializer, - }; - let root_view = (0, l_bound, r_bound); - (arq, root_view) - } - - pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> (Self, ArqView) { - let initializer = Box::new(|_, _| T::identity()); - Self::new(l_bound, r_bound, is_persistent, initializer) - } - - fn push(&mut self, (p, l, r): ArqView) -> (ArqView, ArqView) { - let m = (l + r) / 2; - let (lp, rp) = match self.nodes[p].down { - Some(children) => children, - None => { - let l_val = (self.initializer)(l, m); - let r_val = (self.initializer)(m + 1, r); - self.nodes.push(DynamicArqNode::new(l_val)); - self.nodes.push(DynamicArqNode::new(r_val)); - (self.nodes.len() - 2, self.nodes.len() - 1) - } - }; - if let Some(ref f) = self.nodes[p].app.take() { - self.nodes[lp].apply(f, l == m); - self.nodes[rp].apply(f, m + 1 == r); - } - ((lp, l, m), (rp, m + 1, r)) - } - - fn pull(&mut self, p: usize) { - let (lp, rp) = self.nodes[p].down.unwrap(); - let left_val = &self.nodes[lp].val; - let right_val = &self.nodes[rp].val; - self.nodes[p].val = T::op(left_val, right_val); - } - - fn clone_node(&mut self, p: usize) -> usize { - if self.is_persistent { - let node = self.nodes[p].clone(); - self.nodes.push(node); - self.nodes.len() - 1 - } else { - p - } - } - - /// Applies the endomorphism f to all entries from l to r, inclusive. - /// If l == r, the updates are eager. Otherwise, they are lazy. - pub fn modify(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { - let (p, cl, cr) = view; - if r < cl || cr < l { - view - } else if l <= cl && cr <= r /* && self.l == self.r forces eager */ { - let p_clone = self.clone_node(p); - self.nodes[p_clone].apply(f, l == r); - (p_clone, cl, cr) - } else { - let (l_view, r_view) = self.push(view); - let p_clone = self.clone_node(p); - let lp_clone = self.modify(l_view, l, r, f).0; - let rp_clone = self.modify(r_view, l, r, f).0; - self.nodes[p_clone].down = Some((lp_clone, rp_clone)); - self.pull(p_clone); - (p_clone, cl, cr) - } - } - - /// Returns the aggregate range query on all entries from l to r, inclusive. - pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::M { - let (p, cl, cr) = view; - if r < cl || cr < l { - T::identity() - } else if l <= cl && cr <= r { - T::op(&T::identity(), &self.nodes[p].val) - } else { - let (l_view, r_view) = self.push(view); - let l_agg = self.query(l_view, l, r); - let r_agg = self.query(r_view, l, r); - T::op(&l_agg, &r_agg) - } - } -} - -pub trait ArqSpec { - /// Type of data representing an endomorphism. - // Note that while a Fn(M) -> M may seem like a more natural representation - // for an endomorphism, compositions would then have to delegate to each of - // their parts. This representation is more efficient. - type F: Clone; - /// Type of monoid elements. - type M; - - /// For eager updates, compose() ho be unimplemented!(). For lazy updates: - /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) - fn compose(f: &Self::F, g: &Self::F) -> Self::F; - /// For eager updates, apply() can assume to act on a leaf. For lazy updates: - /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) - fn apply(f: &Self::F, a: &Self::M) -> Self::M; - /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) - fn op(a: &Self::M, b: &Self::M) -> Self::M; - /// Require for all a: op(a, identity()) = op(identity(), a) = a - fn identity() -> Self::M; -} - -/// Range Minimum Query (RMQ), a classic application of ARQ. -/// modify(l, r, &f) sets all entries a[l..=r] to f. -/// query(l, r) finds the minimum value in a[l..=r]. -// -// Exercises: try augmenting this struct to find the index of a minimum element -// in a range query, as well as the number of elements equal to the minimum. -// Then instead of overwriting values with a constant assignment a[i] = f, -// try supporting addition: a[i] += f. -pub enum AssignMin {} -impl ArqSpec for AssignMin { - type F = i64; - type M = i64; - fn compose(&f: &Self::F, _: &Self::F) -> Self::F { - f - } - fn apply(&f: &Self::F, _: &Self::M) -> Self::M { - f - } - fn op(&a: &Self::M, &b: &Self::M) -> Self::M { - a.min(b) - } - fn identity() -> Self::M { - Self::M::max_value() - } -} - -/// An example of binary search on the tree of a StaticArq. -/// In this case, we use RMQ to locate the leftmost negative element. -/// To ensure the existence of a valid root note (i == 1) from which to descend, -/// the tree's size must be a power of two. -pub fn first_negative_static(arq: &mut StaticArq) -> i32 { - assert!(arq.app.len().is_power_of_two()); - let mut p = 1; - if arq.val[p] >= 0 { - return -1; - } - while p < arq.app.len() { - arq.push(p); - p <<= 1; - if arq.val[p] >= 0 { - p |= 1; - } - } - let pos = p - arq.app.len(); - pos as i32 -} - -/// An example of binary search on the tree of a DynamicArq. -/// The tree may have any size, not necessarily a power of two. -pub fn first_negative_dynamic(arq: &mut DynamicArq, view: ArqView) -> i64 { - let (p, cl, cr) = view; - if arq.nodes[p].val >= 0 { - -1 - } else if cl == cr { - cl - } else { - let (l_view, r_view) = arq.push(view); - let lp = l_view.0; - if arq.nodes[lp].val < 0 { - first_negative_dynamic(arq, l_view) - } else { - first_negative_dynamic(arq, r_view) - } - } -} - -/// Range Sum Query, a slightly trickier classic application of ARQ. -/// modify(l, r, &f) sets all entries a[l..=r] to f. -/// query(l, r) sums all the entries a[l..=r]. -/// -/// # Panics -/// -/// Associated functions will panic on overflow. -// -// Note that the apply() operation on raw entries is undefined: while leaf nodes -// should simply be set to f, internal nodes must be set to f * size_of_subtree. -// Thus, our monoid type M should store the pair (entry, size_of_subtree). -// -// In mathematical jargon, we say that constant assignment f(a) = f is not an -// endomorphism on (i64, +) because f(a+b) = f != 2*f = f(a) + f(b). -// On the other hand, f((a, s)) = (f*s, s) is indeed an endomorphism on pairs -// with vector addition: f((a, s) + (b, t)) = f((a+b, s+t)) = (f*(s+t), s+t) -// = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). -pub enum AssignSum {} -impl ArqSpec for AssignSum { - type F = i64; - type M = (i64, i64); - fn compose(&f: &Self::F, _: &Self::F) -> Self::F { - f - } - fn apply(&f: &Self::F, &(_, s): &Self::M) -> Self::M { - (f * s, s) - } - fn op(&(a, s): &Self::M, &(b, t): &Self::M) -> Self::M { - (a + b, s + t) - } - fn identity() -> Self::M { - (0, 0) - } -} - -/// Supply & Demand, based on https://codeforces.com/gym/102218/problem/F -/// modify(i, i, &(p, o)) increases supply by p and demand by o at time i. -/// query(l, r) computes total supply and demand at times l to r, as well as -// how much of the supply is subsequently met by the demand. -// -// Note that the apply() operation is only correct when applied to leaf nodes. -// Therefore, modify() must only be used in "eager" mode, i.e., with l == r. -// compose() should be unimplemented!() to prevent accidental "lazy" updates. -pub enum SupplyDemand {} -impl ArqSpec for SupplyDemand { - type F = (i64, i64); - type M = (i64, i64, i64); // production, orders, sales - fn compose(_: &Self::F, _: &Self::F) -> Self::F { - unimplemented!() - } - fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::M) -> Self::M { - let p = p + p_add; - let o = o + o_add; - (p, o, p.min(o)) - } - fn op((p1, o1, s1): &Self::M, (p2, o2, s2): &Self::M) -> Self::M { - let extra = (p1 - s1).min(o2 - s2); - (p1 + p2, o1 + o2, s1 + s2 + extra) - } - fn identity() -> Self::M { - (0, 0, 0) - } -} - -/// A generic implementation of Mo's algorithm, aka Query Sqrt Decomposition. -/// It answers q offline queries over intervals in 0..n by shifting the query -/// interval's endpoints by one position at a time. -/// Each endpoint incurs a total cost of at most n * sqrt(q * L_OP * R_OP). -pub trait MoState { - type Q; - type A; - - /// cost ratio L_OP / R_OP between a left endpoint and a right endpoint move - const L_R_RATIO: f64 = 1.0; - - fn query(&self, q: &Self::Q) -> Self::A; - fn insert_left(&mut self, pos: usize); - fn remove_left(&mut self, pos: usize); - - fn insert_right(&mut self, pos: usize) { - self.insert_left(pos); - } - fn remove_right(&mut self, pos: usize) { - self.remove_left(pos); - } - /// After initializing self to a state corresponding to an empty interval, - /// call this function to answer all your queries. - fn process(&mut self, queries: &[(usize, usize, Self::Q)]) -> Vec { - let q = queries.len(); - let mut q_positions: Vec = (0..q).collect(); - if let Some(max_r) = queries.iter().map(|&(_, r, _)| r).max() { - let q_adjusted = q as f64 * Self::L_R_RATIO; - let bucket_width = 1 + max_r / q_adjusted.sqrt() as usize; - q_positions.sort_unstable_by_key(|&i| { - let (l, mut r) = (queries[i].0, queries[i].1); - let bucket = l / bucket_width; - if bucket % 2 != 0 { - r = max_r - r; - } - (bucket, r) - }); - } - - let (mut cur_l, mut cur_r) = (1, 0); - let mut answers = Vec::with_capacity(queries.len()); - for i in q_positions { - let (l, r, ref q) = queries[i]; - while cur_l > l { - cur_l -= 1; - self.insert_left(cur_l); - } - while cur_r < r { - cur_r += 1; - self.insert_right(cur_r); - } - while cur_l < l { - self.remove_left(cur_l); - cur_l += 1; - } - while cur_r > r { - self.remove_right(cur_r); - cur_r -= 1; - } - answers.push((i, self.query(q))); - } - answers.sort_unstable_by_key(|&(i, _)| i); - answers.into_iter().map(|(_, ans)| ans).collect() - } -} - -pub struct DistinctVals { - vals: Vec, - counts: Vec, - distinct: usize, -} -impl DistinctVals { - pub fn new(vals: Vec) -> Self { - let &max_val = vals.iter().max().unwrap_or(&0); - Self { - vals, - counts: vec![0; max_val + 1], - distinct: 0, - } - } -} -impl MoState for DistinctVals { - type Q = (); - type A = usize; - fn query(&self, _: &Self::Q) -> Self::A { - self.distinct - } - fn insert_left(&mut self, pos: usize) { - let v = self.vals[pos]; - if self.counts[v] == 0 { - self.distinct += 1; - } - self.counts[v] += 1; - } - fn remove_left(&mut self, pos: usize) { - let v = self.vals[pos]; - self.counts[v] -= 1; - if self.counts[v] == 0 { - self.distinct -= 1; - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_rmq() { - let mut arq = StaticArq::::new(vec![0; 10]); - - assert_eq!(arq.query(0, 9), 0); - - arq.modify(2, 4, &-5); - arq.modify(5, 7, &-3); - arq.modify(1, 6, &1); - - assert_eq!(arq.query(0, 9), -3); - } - - #[test] - fn test_dynamic_rmq() { - let initializer = Box::new(|_, _| 0); - let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); - - assert_eq!(arq.query(view, 0, 9), 0); - - arq.modify(view, 2, 4, &-5); - arq.modify(view, 5, 7, &-3); - arq.modify(view, 1, 6, &1); - - assert_eq!(arq.query(view, 0, 9), -3); - } - - #[test] - fn test_persistent_rmq() { - let initializer = Box::new(|_, _| 0); - let (mut arq, mut view) = DynamicArq::::new(0, 9, true, initializer); - - let at_init = view; - view = arq.modify(view, 2, 4, &-5); - let snapshot = view; - view = arq.modify(view, 5, 7, &-3); - view = arq.modify(view, 1, 6, &1); - - assert_eq!(arq.query(at_init, 0, 9), 0); - assert_eq!(arq.query(snapshot, 0, 9), -5); - assert_eq!(arq.query(view, 0, 9), -3); - } - - #[test] - fn test_range_sum() { - let mut arq = StaticArq::::new(vec![(0, 1); 10]); - - assert_eq!(arq.query(0, 9), (0, 10)); - - arq.modify(1, 3, &10); - arq.modify(3, 5, &1); - - assert_eq!(arq.query(0, 9), (23, 10)); - assert_eq!(arq.query(10, 4), (0, 0)); - } - - #[test] - fn test_dynamic_range_sum() { - let initializer = Box::new(|l, r| (0, 1 + r - l)); - let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); - - assert_eq!(arq.query(view, 0, 9), (0, 10)); - - arq.modify(view, 1, 3, &10); - arq.modify(view, 3, 5, &1); - - assert_eq!(arq.query(view, 0, 9), (23, 10)); - assert_eq!(arq.query(view, 10, 4), (0, 0)); - } - - #[test] - fn test_supply_demand() { - let mut arq = StaticArq::::new(vec![(0, 0, 0); 10]); - - arq.modify(1, 1, &(25, 100)); - arq.modify(3, 3, &(100, 30)); - arq.modify(9, 9, &(0, 20)); - - assert_eq!(arq.query(0, 9), (125, 150, 75)); - } - - #[test] - fn test_dynamic_supply_demand() { - let (mut arq, view) = DynamicArq::::new_with_identity(0, 9, false); - - arq.modify(view, 1, 1, &(25, 100)); - arq.modify(view, 3, 3, &(100, 30)); - arq.modify(view, 9, 9, &(0, 20)); - - assert_eq!(arq.query(view, 0, 9), (125, 150, 75)); - } - - #[test] - fn test_binary_search_rmq() { - let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; - let mut arq = StaticArq::::new(vec); - let pos = first_negative_static(&mut arq); - - arq.modify(3, 7, &0); - let pos_zeros = first_negative_static(&mut arq); - - assert_eq!(pos, 3); - assert_eq!(pos_zeros, -1); - } - - #[test] - fn test_dynamic_binary_search_rmq() { - let initializer = Box::new(|_, r| 2 - r); - let (l_bound, r_bound) = (0, 1_000_000_000_000_000_000); - let (mut arq, view) = DynamicArq::::new(l_bound, r_bound, false, initializer); - let pos = first_negative_dynamic(&mut arq, view); - - arq.modify(view, 2, r_bound - 1, &0); - let pos_mostly_zeros = first_negative_dynamic(&mut arq, view); - arq.modify(view, 2, r_bound, &0); - let pos_zeros = first_negative_dynamic(&mut arq, view); - - assert_eq!(pos, 3); - assert_eq!(pos_mostly_zeros, r_bound); - assert_eq!(pos_zeros, -1); - } - - #[test] - fn test_mos_algorithm() { - let queries = vec![(0, 2, ()), (5, 5, ()), (2, 6, ()), (0, 6, ())]; - let arr = vec![4, 8, 4, 7, 1, 9, 8]; - - let answers = DistinctVals::new(arr).process(&queries); - - assert_eq!(answers, vec![2, 1, 5, 5]); - } -} diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 8c52c5d..0cd2af6 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -24,11 +24,11 @@ impl FlowGraph { } } - /// Adds an edge with specified capacity and cost. The reverse edge is also - /// added for residual graph computation, but has zero capacity. - pub fn add_edge(&mut self, u: usize, v: usize, cap: i64, cost: i64) { + /// Adds an edge with specified directional capacities and cost per unit of + /// flow. If only forward flow is allowed, rcap should be zero. + pub fn add_edge(&mut self, u: usize, v: usize, cap: i64, rcap: i64, cost: i64) { self.cap.push(cap); - self.cap.push(0); + self.cap.push(rcap); self.cost.push(cost); self.cost.push(-cost); self.graph.add_undirected_edge(u, v); @@ -203,8 +203,8 @@ mod test { #[test] fn test_basic_flow() { let mut graph = FlowGraph::new(3, 2); - graph.add_edge(0, 1, 4, 1); - graph.add_edge(1, 2, 3, 1); + graph.add_edge(0, 1, 4, 0, 0); + graph.add_edge(1, 2, 3, 0, 0); let flow = graph.dinic(0, 2).0; assert_eq!(flow, 3); @@ -213,10 +213,10 @@ mod test { #[test] fn test_min_cost_flow() { let mut graph = FlowGraph::new(4, 4); - graph.add_edge(0, 1, 10, -10); - graph.add_edge(1, 2, 7, 8); - graph.add_edge(2, 3, 7, 8); - graph.add_edge(1, 3, 7, 10); + graph.add_edge(0, 1, 10, 0, -10); + graph.add_edge(1, 2, 7, 0, 8); + graph.add_edge(2, 3, 7, 0, 8); + graph.add_edge(1, 3, 7, 0, 10); let (cost, flow, _) = graph.mcf(0, 3); assert_eq!(cost, 18); @@ -237,21 +237,21 @@ mod test { //Initialize source / sink connections; both left & right have 6 nodes for lhs_vertex in left_start..left_start + 6 { - graph.add_edge(source, lhs_vertex, 1, 1); + graph.add_edge(source, lhs_vertex, 1, 0, 0); } for rhs_vertex in right_start..right_start + 6 { - graph.add_edge(rhs_vertex, sink, 1, 1); + graph.add_edge(rhs_vertex, sink, 1, 0, 0); } - graph.add_edge(left_start + 0, right_start + 1, 1, 1); - graph.add_edge(left_start + 0, right_start + 2, 1, 1); - graph.add_edge(left_start + 2, right_start + 0, 1, 1); - graph.add_edge(left_start + 2, right_start + 3, 1, 1); - graph.add_edge(left_start + 3, right_start + 2, 1, 1); - graph.add_edge(left_start + 4, right_start + 2, 1, 1); - graph.add_edge(left_start + 4, right_start + 3, 1, 1); - graph.add_edge(left_start + 5, right_start + 5, 1, 1); + graph.add_edge(left_start + 0, right_start + 1, 1, 0, 0); + graph.add_edge(left_start + 0, right_start + 2, 1, 0, 0); + graph.add_edge(left_start + 2, right_start + 0, 1, 0, 0); + graph.add_edge(left_start + 2, right_start + 3, 1, 0, 0); + graph.add_edge(left_start + 3, right_start + 2, 1, 0, 0); + graph.add_edge(left_start + 4, right_start + 2, 1, 0, 0); + graph.add_edge(left_start + 4, right_start + 3, 1, 0, 0); + graph.add_edge(left_start + 5, right_start + 5, 1, 0, 0); let (flow_amt, flow) = graph.dinic(source, sink); assert_eq!(flow_amt, 5); diff --git a/src/lib.rs b/src/lib.rs index b7fa839..2e50a88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! Algorithms Cookbook in Rust. -pub mod arq_tree; pub mod graph; pub mod math; +pub mod range_query; pub mod scanner; pub mod string_proc; diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs new file mode 100644 index 0000000..92783c6 --- /dev/null +++ b/src/range_query/dynamic_arq.rs @@ -0,0 +1,161 @@ +//! Associative Range Query Tree with dynamic allocation, supporting dynamic +//! node construction and persistence +use super::ArqSpec; + +pub struct DynamicArqNode { + val: T::M, + app: Option, + down: (usize, usize), +} + +// TODO: can this be replaced by a #[derive(Clone)]? +impl Clone for DynamicArqNode { + fn clone(&self) -> Self { + Self { + val: T::op(&T::identity(), &self.val), + app: self.app.clone(), + down: self.down, + } + } +} + +impl DynamicArqNode { + pub fn new(val: T::M) -> Self { + Self { + val, + app: None, + down: (0, 0), + } + } + + fn apply(&mut self, f: &T::F, is_leaf: bool) { + self.val = T::apply(f, &self.val); + if !is_leaf { + let h = match self.app { + Some(ref g) => T::compose(f, g), + None => f.clone(), + }; + self.app = Some(h); + } + } +} + +pub type ArqView = (usize, i64, i64); + +/// A dynamic, and optionally persistent, associative range query data structure. +pub struct DynamicArq { + nodes: Vec>, + is_persistent: bool, + initializer: Box T::M>, +} + +impl DynamicArq { + pub fn new( + l_bound: i64, + r_bound: i64, + is_persistent: bool, + initializer: Box T::M>, + ) -> (Self, ArqView) { + let val = initializer(l_bound, r_bound); + let nodes = vec![DynamicArqNode::new(val)]; + let arq = Self { + nodes, + is_persistent, + initializer, + }; + let root_view = (0, l_bound, r_bound); + (arq, root_view) + } + + pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> (Self, ArqView) { + let initializer = Box::new(|_, _| T::identity()); + Self::new(l_bound, r_bound, is_persistent, initializer) + } + + pub fn push(&mut self, (p, l, r): ArqView) -> (ArqView, ArqView) { + let m = (l + r) / 2; + if self.nodes[p].down.0 == 0 { + let l_val = (self.initializer)(l, m); + let r_val = (self.initializer)(m + 1, r); + self.nodes.push(DynamicArqNode::new(l_val)); + self.nodes.push(DynamicArqNode::new(r_val)); + self.nodes[p].down = (self.nodes.len() - 2, self.nodes.len() - 1) + }; + let (lp, rp) = self.nodes[p].down; + if let Some(ref f) = self.nodes[p].app.take() { + self.nodes[lp].apply(f, l == m); + self.nodes[rp].apply(f, m + 1 == r); + } + ((lp, l, m), (rp, m + 1, r)) + } + + fn pull(&mut self, p: usize) { + let (lp, rp) = self.nodes[p].down; + let left_val = &self.nodes[lp].val; + let right_val = &self.nodes[rp].val; + self.nodes[p].val = T::op(left_val, right_val); + } + + fn clone_node(&mut self, p: usize) -> usize { + if self.is_persistent { + let node = self.nodes[p].clone(); + self.nodes.push(node); + self.nodes.len() - 1 + } else { + p + } + } + + /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. + pub fn modify(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { + let (p, cl, cr) = view; + if r < cl || cr < l { + view + } else if l <= cl && cr <= r /* && self.l == self.r forces eager */ { + let p_clone = self.clone_node(p); + self.nodes[p_clone].apply(f, l == r); + (p_clone, cl, cr) + } else { + let (l_view, r_view) = self.push(view); + let p_clone = self.clone_node(p); + let lp_clone = self.modify(l_view, l, r, f).0; + let rp_clone = self.modify(r_view, l, r, f).0; + self.nodes[p_clone].down = (lp_clone, rp_clone); + self.pull(p_clone); + (p_clone, cl, cr) + } + } + + /// Returns the aggregate range query on all entries from l to r, inclusive. + pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::M { + let (p, cl, cr) = view; + if r < cl || cr < l { + T::identity() + } else if l <= cl && cr <= r { + T::op(&T::identity(), &self.nodes[p].val) + } else { + let (l_view, r_view) = self.push(view); + let l_agg = self.query(l_view, l, r); + let r_agg = self.query(r_view, l, r); + T::op(&l_agg, &r_agg) + } + } +} + +/// An example of binary search on the tree of a DynamicArq. +/// The tree may have any size, not necessarily a power of two. +pub fn first_negative(arq: &mut DynamicArq, view: ArqView) -> Option { + let (p, cl, cr) = view; + if cl == cr { + Some(cl).filter(|_| arq.nodes[p].val < 0) + } else { + let (l_view, r_view) = arq.push(view); + let lp = l_view.0; + if arq.nodes[lp].val < 0 { + first_negative(arq, l_view) + } else { + first_negative(arq, r_view) + } + } +} diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs new file mode 100644 index 0000000..0b9180a --- /dev/null +++ b/src/range_query/mod.rs @@ -0,0 +1,135 @@ +pub mod dynamic_arq; +pub mod specs; +pub mod sqrt_decomp; +pub mod static_arq; +pub use dynamic_arq::{ArqView, DynamicArq}; +pub use specs::ArqSpec; +pub use static_arq::StaticArq; + +#[cfg(test)] +mod test { + use super::specs::*; + use super::*; + + #[test] + fn test_rmq() { + let mut arq = StaticArq::::new(vec![0; 10]); + + assert_eq!(arq.query(0, 9), 0); + + arq.modify(2, 4, &-5); + arq.modify(5, 7, &-3); + arq.modify(1, 6, &1); + + assert_eq!(arq.query(0, 9), -3); + } + + #[test] + fn test_dynamic_rmq() { + let initializer = Box::new(|_, _| 0); + let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); + + assert_eq!(arq.query(view, 0, 9), 0); + + arq.modify(view, 2, 4, &-5); + arq.modify(view, 5, 7, &-3); + arq.modify(view, 1, 6, &1); + + assert_eq!(arq.query(view, 0, 9), -3); + } + + #[test] + fn test_persistent_rmq() { + let initializer = Box::new(|_, _| 0); + let (mut arq, mut view) = DynamicArq::::new(0, 9, true, initializer); + + let at_init = view; + view = arq.modify(view, 2, 4, &-5); + let snapshot = view; + view = arq.modify(view, 5, 7, &-3); + view = arq.modify(view, 1, 6, &1); + + assert_eq!(arq.query(at_init, 0, 9), 0); + assert_eq!(arq.query(snapshot, 0, 9), -5); + assert_eq!(arq.query(view, 0, 9), -3); + } + + #[test] + fn test_range_sum() { + let mut arq = StaticArq::::new(vec![(0, 1); 10]); + + assert_eq!(arq.query(0, 9), (0, 10)); + + arq.modify(1, 3, &10); + arq.modify(3, 5, &1); + + assert_eq!(arq.query(0, 9), (23, 10)); + assert_eq!(arq.query(10, 4), (0, 0)); + } + + #[test] + fn test_dynamic_range_sum() { + let initializer = Box::new(|l, r| (0, 1 + r - l)); + let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); + + assert_eq!(arq.query(view, 0, 9), (0, 10)); + + arq.modify(view, 1, 3, &10); + arq.modify(view, 3, 5, &1); + + assert_eq!(arq.query(view, 0, 9), (23, 10)); + assert_eq!(arq.query(view, 10, 4), (0, 0)); + } + + #[test] + fn test_supply_demand() { + let mut arq = StaticArq::::new(vec![(0, 0, 0); 10]); + + arq.modify(1, 1, &(25, 100)); + arq.modify(3, 3, &(100, 30)); + arq.modify(9, 9, &(0, 20)); + + assert_eq!(arq.query(0, 9), (125, 150, 75)); + } + + #[test] + fn test_dynamic_supply_demand() { + let (mut arq, view) = DynamicArq::::new_with_identity(0, 9, false); + + arq.modify(view, 1, 1, &(25, 100)); + arq.modify(view, 3, 3, &(100, 30)); + arq.modify(view, 9, 9, &(0, 20)); + + assert_eq!(arq.query(view, 0, 9), (125, 150, 75)); + } + + #[test] + fn test_binary_search_rmq() { + let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; + let mut arq = StaticArq::::new(vec); + let first_neg = static_arq::first_negative(&mut arq); + + arq.modify(3, 7, &0); + let first_neg_zeros = static_arq::first_negative(&mut arq); + + assert_eq!(first_neg, Some(3)); + assert_eq!(first_neg_zeros, None); + } + + #[test] + fn test_dynamic_binary_search_rmq() { + let initializer = Box::new(|_, r| 2 - r); + let (l_bound, r_bound) = (0, 1_000_000_000_000_000_000); + let (mut arq, view) = DynamicArq::::new(l_bound, r_bound, false, initializer); + let first_neg = dynamic_arq::first_negative(&mut arq, view); + + arq.modify(view, 2, r_bound - 1, &0); + let first_neg_late = dynamic_arq::first_negative(&mut arq, view); + arq.modify(view, 2, r_bound, &0); + let first_neg_zeros = dynamic_arq::first_negative(&mut arq, view); + + assert_eq!(first_neg, Some(3)); + assert_eq!(first_neg_late, Some(r_bound)); + assert_eq!(first_neg_zeros, None); + } +} diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs new file mode 100644 index 0000000..b26512b --- /dev/null +++ b/src/range_query/specs.rs @@ -0,0 +1,112 @@ +//! A collection of example ArqSpec implementations + +pub trait ArqSpec { + /// Type of data representing an endomorphism. + // Note that while a Fn(M) -> M may seem like a more natural representation + // for an endomorphism, compositions would then have to delegate to each of + // their parts. This representation is more efficient. + type F: Clone; + /// Type of monoid elements. + type M; + + /// For eager updates, compose() ho be unimplemented!(). For lazy updates: + /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) + fn compose(f: &Self::F, g: &Self::F) -> Self::F; + /// For eager updates, apply() can assume to act on a leaf. For lazy updates: + /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) + fn apply(f: &Self::F, a: &Self::M) -> Self::M; + /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) + fn op(a: &Self::M, b: &Self::M) -> Self::M; + /// Require for all a: op(a, identity()) = op(identity(), a) = a + fn identity() -> Self::M; +} + +/// Range Minimum Query (RMQ), a classic application of ARQ. +/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// query(l, r) finds the minimum value in a[l..=r]. +// +// Exercises: try augmenting this struct to find the index of a minimum element +// in a range query, as well as the number of elements equal to the minimum. +// Then instead of overwriting values with a constant assignment a[i] = f, +// try supporting addition: a[i] += f. +pub enum AssignMin {} +impl ArqSpec for AssignMin { + type F = i64; + type M = i64; + fn compose(&f: &Self::F, _: &Self::F) -> Self::F { + f + } + fn apply(&f: &Self::F, _: &Self::M) -> Self::M { + f + } + fn op(&a: &Self::M, &b: &Self::M) -> Self::M { + a.min(b) + } + fn identity() -> Self::M { + i64::max_value() + } +} + +/// Range Sum Query, a slightly trickier classic application of ARQ. +/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// query(l, r) sums all the entries a[l..=r]. +/// +/// # Panics +/// +/// Associated functions will panic on overflow. +// +// Note that the apply() operation on raw entries is undefined: while leaf nodes +// should simply be set to f, internal nodes must be set to f * size_of_subtree. +// Thus, our monoid type M should store the pair (entry, size_of_subtree). +// +// In mathematical jargon, we say that constant assignment f(a) = f is not an +// endomorphism on (i64, +) because f(a+b) = f != 2*f = f(a) + f(b). +// On the other hand, f((a, s)) = (f*s, s) is indeed an endomorphism on pairs +// with vector addition: f((a, s) + (b, t)) = f((a+b, s+t)) = (f*(s+t), s+t) +// = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). +pub enum AssignSum {} +impl ArqSpec for AssignSum { + type F = i64; + type M = (i64, i64); + fn compose(&f: &Self::F, _: &Self::F) -> Self::F { + f + } + fn apply(&f: &Self::F, &(_, s): &Self::M) -> Self::M { + (f * s, s) + } + fn op(&(a, s): &Self::M, &(b, t): &Self::M) -> Self::M { + (a + b, s + t) + } + fn identity() -> Self::M { + (0, 0) + } +} + +/// Supply & Demand, based on https://codeforces.com/gym/102218/problem/F +/// modify(i, i, &(p, o)) increases supply by p and demand by o at time i. +/// query(l, r) computes total supply and demand at times l to r, as well as +// how much of the supply is subsequently met by the demand. +// +// Note that the apply() operation is only correct when applied to leaf nodes. +// Therefore, modify() must only be used in "eager" mode, i.e., with l == r. +// compose() should be unimplemented!() to prevent accidental "lazy" updates. +pub enum SupplyDemand {} +impl ArqSpec for SupplyDemand { + type F = (i64, i64); + type M = (i64, i64, i64); // production, orders, sales + fn compose(_: &Self::F, _: &Self::F) -> Self::F { + unimplemented!() + } + fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::M) -> Self::M { + let p = p + p_add; + let o = o + o_add; + (p, o, p.min(o)) + } + fn op((p1, o1, s1): &Self::M, (p2, o2, s2): &Self::M) -> Self::M { + let extra = (p1 - s1).min(o2 - s2); + (p1 + p2, o1 + o2, s1 + s2 + extra) + } + fn identity() -> Self::M { + (0, 0, 0) + } +} diff --git a/src/range_query/sqrt_decomp.rs b/src/range_query/sqrt_decomp.rs new file mode 100644 index 0000000..5ba2497 --- /dev/null +++ b/src/range_query/sqrt_decomp.rs @@ -0,0 +1,118 @@ +//! A generic implementation of Mo's algorithm, aka Query Sqrt Decomposition. +//! It answers q offline queries over intervals in 0..n by shifting the query +//! interval's endpoints by one position at a time. +//! Each endpoint incurs a total cost of at most n * sqrt(q * L_OP * R_OP). + +pub trait MoState { + type Q; + type A; + + /// cost ratio L_OP / R_OP between a left endpoint and a right endpoint move + const L_R_RATIO: f64 = 1.0; + + fn query(&self, q: &Self::Q) -> Self::A; + fn insert_left(&mut self, pos: usize); + fn remove_left(&mut self, pos: usize); + + fn insert_right(&mut self, pos: usize) { + self.insert_left(pos); + } + fn remove_right(&mut self, pos: usize) { + self.remove_left(pos); + } + /// After initializing self to a state corresponding to an empty interval, + /// call this function to answer all your queries. + fn process(&mut self, queries: &[(usize, usize, Self::Q)]) -> Vec { + let q = queries.len(); + let mut q_positions: Vec = (0..q).collect(); + if let Some(max_r) = queries.iter().map(|&(_, r, _)| r).max() { + let q_adjusted = q as f64 * Self::L_R_RATIO; + let bucket_width = 1 + max_r / q_adjusted.sqrt() as usize; + q_positions.sort_unstable_by_key(|&i| { + let (l, mut r) = (queries[i].0, queries[i].1); + let bucket = l / bucket_width; + if bucket % 2 != 0 { + r = max_r - r; + } + (bucket, r) + }); + } + + let (mut cur_l, mut cur_r) = (1, 0); + let mut answers = Vec::with_capacity(queries.len()); + for i in q_positions { + let (l, r, ref q) = queries[i]; + while cur_l > l { + cur_l -= 1; + self.insert_left(cur_l); + } + while cur_r < r { + cur_r += 1; + self.insert_right(cur_r); + } + while cur_l < l { + self.remove_left(cur_l); + cur_l += 1; + } + while cur_r > r { + self.remove_right(cur_r); + cur_r -= 1; + } + answers.push((i, self.query(q))); + } + answers.sort_unstable_by_key(|&(i, _)| i); + answers.into_iter().map(|(_, ans)| ans).collect() + } +} + +pub struct DistinctVals { + vals: Vec, + counts: Vec, + distinct: usize, +} +impl DistinctVals { + pub fn new(vals: Vec) -> Self { + let &max_val = vals.iter().max().unwrap_or(&0); + Self { + vals, + counts: vec![0; max_val + 1], + distinct: 0, + } + } +} +impl MoState for DistinctVals { + type Q = (); + type A = usize; + fn query(&self, _: &Self::Q) -> Self::A { + self.distinct + } + fn insert_left(&mut self, pos: usize) { + let v = self.vals[pos]; + if self.counts[v] == 0 { + self.distinct += 1; + } + self.counts[v] += 1; + } + fn remove_left(&mut self, pos: usize) { + let v = self.vals[pos]; + self.counts[v] -= 1; + if self.counts[v] == 0 { + self.distinct -= 1; + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_mos_algorithm() { + let queries = vec![(0, 2, ()), (5, 5, ()), (2, 6, ()), (0, 6, ())]; + let arr = vec![4, 8, 4, 7, 1, 9, 8]; + + let answers = DistinctVals::new(arr).process(&queries); + + assert_eq!(answers, vec![2, 1, 5, 5]); + } +} diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs new file mode 100644 index 0000000..c86b675 --- /dev/null +++ b/src/range_query/static_arq.rs @@ -0,0 +1,153 @@ +//! Associative Range Query Tree based on [Al.Cash's compact representation] +//! (http://codeforces.com/blog/entry/18051). +use super::ArqSpec; + +/// Colloquially known as a "segtree" in the sport programming literature, it +/// represents a sequence of elements a_i (0 <= i < size) from a monoid (M, +) +/// on which we want to support fast range operations: +/// +/// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f +/// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r +/// +/// Future work: ArqTree would lend itself naturally to Rust's ownership system. +/// Initially, we should only have access to the root nodes: +/// if size is a power of two, there is a unique root at index 1. +/// arq.push(i) locks i and acquires access to its children. +/// arq.pull(i) is called when the lock on i is released. +pub struct StaticArq { + val: Vec, + app: Vec>, +} + +impl StaticArq { + /// Initializes a static balanced tree on top of the given sequence. + pub fn new(init_val: Vec) -> Self { + let size = init_val.len(); + let mut val = (0..size).map(|_| T::identity()).collect::>(); + val.append(&mut { init_val }); + let app = vec![None; size]; + + let mut arq = Self { val, app }; + for p in (0..size).rev() { + arq.pull(p); + } + arq + } + + fn apply(&mut self, p: usize, f: &T::F) { + self.val[p] = T::apply(f, &self.val[p]); + if let Some(lazy) = self.app.get_mut(p) { + let h = match *lazy { + Some(ref g) => T::compose(f, g), + None => f.clone(), + }; + *lazy = Some(h); + } + } + + fn push(&mut self, p: usize) { + if let Some(ref f) = self.app[p].take() { + self.apply(p << 1, f); + self.apply(p << 1 | 1, f); + } + } + + fn pull(&mut self, p: usize) { + self.val[p] = T::op(&self.val[p << 1], &self.val[p << 1 | 1]); + } + + fn push_to(&mut self, p: usize) { + let one_plus_floor_log_p = (p + 1).next_power_of_two().trailing_zeros(); + for s in (1..one_plus_floor_log_p).rev() { + self.push(p >> s); + } + } + + fn pull_from(&mut self, mut p: usize) { + while p > 1 { + p >>= 1; + self.pull(p); + } + } + + /// Applies the endomorphism f to all entries from l to r, inclusive. + /// If l == r, the updates are eager. Otherwise, they are lazy. + /// + /// # Panics + /// + /// Panics if r >= size. Note that l > r is valid, meaning an empty range. + pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { + l += self.app.len(); + r += self.app.len(); + if l < r { + self.push_to(l); + } + self.push_to(r); + let (mut l0, mut r0) = (1, 1); + while l <= r { + if l & 1 == 1 { + self.apply(l, f); + l0 = l0.max(l); + l += 1; + } + if r & 1 == 0 { + self.apply(r, f); + r0 = r0.max(r); + r -= 1; + } + l >>= 1; + r >>= 1; + } + self.pull_from(l0); + self.pull_from(r0); + } + + /// Returns the aggregate range query on all entries from l to r, inclusive. + /// + /// # Panics + /// + /// Panics if r >= size. Note that l > r is valid, meaning an empty range. + pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { + l += self.app.len(); + r += self.app.len(); + if l < r { + self.push_to(l); + } + self.push_to(r); + let (mut l_agg, mut r_agg) = (T::identity(), T::identity()); + while l <= r { + if l & 1 == 1 { + l_agg = T::op(&l_agg, &self.val[l]); + l += 1; + } + if r & 1 == 0 { + r_agg = T::op(&self.val[r], &r_agg); + r -= 1; + } + l >>= 1; + r >>= 1; + } + T::op(&l_agg, &r_agg) + } +} + +/// An example of binary search on the tree of a StaticArq. +/// In this case, we use RMQ to locate the leftmost negative element. +/// To ensure the existence of a valid root note (i == 1) from which to descend, +/// the tree's size must be a power of two. +pub fn first_negative(arq: &mut StaticArq) -> Option { + assert!(arq.app.len().is_power_of_two()); + let mut p = 1; + if arq.val[p] >= 0 { + None + } else { + while p < arq.app.len() { + arq.push(p); + p <<= 1; + if arq.val[p] >= 0 { + p |= 1; + } + } + Some(p - arq.app.len()) + } +} diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index c079c86..a5c1721 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,8 +3,8 @@ //! module's contents directly here instead of the use statements. //! Also, replace io::Cursor with io::stdin as shown in scanner.rs. extern crate contest_algorithms; -use contest_algorithms::arq_tree::{AssignSum, StaticArq}; use contest_algorithms::graph::Graph; +use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; use contest_algorithms::scanner::Scanner; const SAMPLE_INPUT: &str = "\ From 47fa6f6a64ea8564e0ec63539524f36a7bcb232f Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 5 Aug 2019 16:31:25 -0700 Subject: [PATCH 36/71] more ways to initialize and merge trees with DynamicArq --- src/range_query/dynamic_arq.rs | 68 +++++++++++++++++++++++++--------- src/range_query/mod.rs | 37 +++++++++++++----- src/range_query/static_arq.rs | 4 +- src/scanner.rs | 8 ++-- tests/codeforces343d.rs | 7 ++-- 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index 92783c6..527a737 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -24,7 +24,7 @@ impl DynamicArqNode { Self { val, app: None, - down: (0, 0), + down: (usize::max_value(), usize::max_value()), } } @@ -50,31 +50,65 @@ pub struct DynamicArq { } impl DynamicArq { - pub fn new( - l_bound: i64, - r_bound: i64, - is_persistent: bool, - initializer: Box T::M>, - ) -> (Self, ArqView) { - let val = initializer(l_bound, r_bound); - let nodes = vec![DynamicArqNode::new(val)]; - let arq = Self { - nodes, + /// Initializes the data structure without creating any nodes. + /// The initializer f must satisfy: f(l, r) = T::op(f(l, m), f(m+1, r)) + pub fn new(is_persistent: bool, initializer: Box T::M>) -> Self { + Self { + nodes: vec![], is_persistent, initializer, - }; - let root_view = (0, l_bound, r_bound); - (arq, root_view) + } } - pub fn new_with_identity(l_bound: i64, r_bound: i64, is_persistent: bool) -> (Self, ArqView) { + /// Initializes the data structure without creating any nodes. + pub fn new_with_identity(is_persistent: bool) -> Self { let initializer = Box::new(|_, _| T::identity()); - Self::new(l_bound, r_bound, is_persistent, initializer) + Self::new(is_persistent, initializer) + } + + /// Lazily builds a tree over the range [l_bound, r_bound]. + pub fn build_using_initializer(&mut self, l_bound: i64, r_bound: i64) -> ArqView { + let view = (self.nodes.len(), l_bound, r_bound); + let val = (self.initializer)(l_bound, r_bound); + self.nodes.push(DynamicArqNode::new(val)); + view + } + + /// Builds a tree whose leaves are set to a given non-empty slice. + pub fn build_from_slice(&mut self, init_val: &[T::M]) -> ArqView { + if init_val.len() == 1 { + let view = (self.nodes.len(), 0, 0); + let val = T::op(&T::identity(), &init_val[0]); + self.nodes.push(DynamicArqNode::new(val)); + return view; + } + let m = (init_val.len() + 1) / 2; + let (l_init, r_init) = init_val.split_at(m); + let l_view = self.build_from_slice(l_init); + let r_view = self.build_from_slice(r_init); + self.merge_equal_sized(l_view, r_view) + } + + /// Merges two balanced subtrees into a single tree with a 0-indexed view. + pub fn merge_equal_sized(&mut self, l_view: ArqView, r_view: ArqView) -> ArqView { + let l_len = l_view.2 - l_view.1 + 1; + let r_len = r_view.2 - r_view.1 + 1; + assert!(l_len == r_len || l_len == r_len + 1); + + let view = (self.nodes.len(), 0, l_len + r_len - 1); + let root = DynamicArqNode { + val: T::identity(), + app: None, + down: (l_view.0, r_view.0), + }; + self.nodes.push(root); + self.pull(view.0); + view } pub fn push(&mut self, (p, l, r): ArqView) -> (ArqView, ArqView) { let m = (l + r) / 2; - if self.nodes[p].down.0 == 0 { + if self.nodes[p].down.0 == usize::max_value() { let l_val = (self.initializer)(l, m); let r_val = (self.initializer)(m + 1, r); self.nodes.push(DynamicArqNode::new(l_val)); diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 0b9180a..09883cf 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -13,7 +13,7 @@ mod test { #[test] fn test_rmq() { - let mut arq = StaticArq::::new(vec![0; 10]); + let mut arq = StaticArq::::new(&[0; 10]); assert_eq!(arq.query(0, 9), 0); @@ -27,7 +27,8 @@ mod test { #[test] fn test_dynamic_rmq() { let initializer = Box::new(|_, _| 0); - let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); + let mut arq = DynamicArq::::new(false, initializer); + let view = arq.build_using_initializer(0, 9); assert_eq!(arq.query(view, 0, 9), 0); @@ -41,7 +42,8 @@ mod test { #[test] fn test_persistent_rmq() { let initializer = Box::new(|_, _| 0); - let (mut arq, mut view) = DynamicArq::::new(0, 9, true, initializer); + let mut arq = DynamicArq::::new(true, initializer); + let mut view = arq.build_using_initializer(0, 9); let at_init = view; view = arq.modify(view, 2, 4, &-5); @@ -56,7 +58,7 @@ mod test { #[test] fn test_range_sum() { - let mut arq = StaticArq::::new(vec![(0, 1); 10]); + let mut arq = StaticArq::::new(&[(0, 1); 10]); assert_eq!(arq.query(0, 9), (0, 10)); @@ -70,7 +72,8 @@ mod test { #[test] fn test_dynamic_range_sum() { let initializer = Box::new(|l, r| (0, 1 + r - l)); - let (mut arq, view) = DynamicArq::::new(0, 9, false, initializer); + let mut arq = DynamicArq::::new(false, initializer); + let view = arq.build_using_initializer(0, 9); assert_eq!(arq.query(view, 0, 9), (0, 10)); @@ -83,7 +86,7 @@ mod test { #[test] fn test_supply_demand() { - let mut arq = StaticArq::::new(vec![(0, 0, 0); 10]); + let mut arq = StaticArq::::new(&[(0, 0, 0); 10]); arq.modify(1, 1, &(25, 100)); arq.modify(3, 3, &(100, 30)); @@ -94,7 +97,8 @@ mod test { #[test] fn test_dynamic_supply_demand() { - let (mut arq, view) = DynamicArq::::new_with_identity(0, 9, false); + let mut arq = DynamicArq::::new_with_identity(false); + let view = arq.build_using_initializer(0, 9); arq.modify(view, 1, 1, &(25, 100)); arq.modify(view, 3, 3, &(100, 30)); @@ -106,7 +110,7 @@ mod test { #[test] fn test_binary_search_rmq() { let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; - let mut arq = StaticArq::::new(vec); + let mut arq = StaticArq::::new(&vec); let first_neg = static_arq::first_negative(&mut arq); arq.modify(3, 7, &0); @@ -116,11 +120,26 @@ mod test { assert_eq!(first_neg_zeros, None); } + #[test] + fn test_dynslice_binary_search_rmq() { + let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; + let mut arq = DynamicArq::::new_with_identity(false); + let view = arq.build_from_slice(&vec); + let first_neg = dynamic_arq::first_negative(&mut arq, view); + + arq.modify(view, 3, 7, &0); + let first_neg_zeros = dynamic_arq::first_negative(&mut arq, view); + + assert_eq!(first_neg, Some(3)); + assert_eq!(first_neg_zeros, None); + } + #[test] fn test_dynamic_binary_search_rmq() { let initializer = Box::new(|_, r| 2 - r); let (l_bound, r_bound) = (0, 1_000_000_000_000_000_000); - let (mut arq, view) = DynamicArq::::new(l_bound, r_bound, false, initializer); + let mut arq = DynamicArq::::new(false, initializer); + let view = arq.build_using_initializer(l_bound, r_bound); let first_neg = dynamic_arq::first_negative(&mut arq, view); arq.modify(view, 2, r_bound - 1, &0); diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index c86b675..4b23711 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -21,10 +21,10 @@ pub struct StaticArq { impl StaticArq { /// Initializes a static balanced tree on top of the given sequence. - pub fn new(init_val: Vec) -> Self { + pub fn new(init_val: &[T::M]) -> Self { let size = init_val.len(); let mut val = (0..size).map(|_| T::identity()).collect::>(); - val.append(&mut { init_val }); + val.extend(init_val.iter().map(|v| T::op(&T::identity(), v))); let app = vec![None; size]; let mut arq = Self { val, app }; diff --git a/src/scanner.rs b/src/scanner.rs index 265e7a2..ae6f5a9 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -86,8 +86,8 @@ mod test { #[test] fn test_in_memory_io() { - let cursor = io::Cursor::new("50 8"); - let mut scan = Scanner::new(cursor); + let input = "50 8".as_bytes(); + let mut scan = Scanner::new(input); let mut out = String::new(); use std::fmt::Write; // needed for writeln!() @@ -100,8 +100,8 @@ mod test { #[test] fn test_in_memory_unsafe() { - let cursor = io::Cursor::new("50 8"); - let mut scan = UnsafeScanner::new(cursor); + let input = "50 8".as_bytes(); + let mut scan = UnsafeScanner::new(input); let mut out = String::new(); use std::fmt::Write; // needed for writeln!() diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index a5c1721..b4f1d94 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -1,7 +1,7 @@ //! Solves [Water Tree](http://codeforces.com/contest/343/problem/D). //! To make a self-contained file for contest submission, dump each desired //! module's contents directly here instead of the use statements. -//! Also, replace io::Cursor with io::stdin as shown in scanner.rs. +//! 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}; @@ -62,8 +62,7 @@ fn dfs( #[test] fn main() { use std::fmt::Write; - let cursor = std::io::Cursor::new(SAMPLE_INPUT); - let mut scan = Scanner::new(cursor); + let mut scan = Scanner::new(SAMPLE_INPUT.as_bytes()); let mut out = String::new(); /* To read/write with stdin/stdout instead: use std::io::{self, Write}; @@ -85,7 +84,7 @@ fn main() { let mut p = vec![0; n]; dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); - let mut arq = StaticArq::::new(vec![(0, 1); n + 1]); + let mut arq = StaticArq::::new(&vec![(0, 1); n + 1]); let q = scan.token::(); for _ in 0..q { let c = scan.token::(); From 42db8cc763bb54814b89c5d8c75e3f5f82b0cf39 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 6 Aug 2019 09:56:14 -0700 Subject: [PATCH 37/71] Cleaning up redundant pieces of DynamicArq, making it pretty much the best segtree you've ever seen :D --- src/range_query/dynamic_arq.rs | 140 +++++++++++++++------------------ src/range_query/mod.rs | 54 ++++++------- src/range_query/specs.rs | 2 +- src/range_query/static_arq.rs | 4 +- 4 files changed, 89 insertions(+), 111 deletions(-) diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index 527a737..97f279e 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -8,29 +8,31 @@ pub struct DynamicArqNode { down: (usize, usize), } -// TODO: can this be replaced by a #[derive(Clone)]? +// TODO: in a future Rust version, this might be replaced by a #[derive(Clone)] impl Clone for DynamicArqNode { fn clone(&self) -> Self { Self { - val: T::op(&T::identity(), &self.val), + val: self.val.clone(), app: self.app.clone(), down: self.down, } } } -impl DynamicArqNode { - pub fn new(val: T::M) -> Self { +impl Default for DynamicArqNode { + fn default() -> Self { Self { - val, + val: T::identity(), app: None, down: (usize::max_value(), usize::max_value()), } } +} - fn apply(&mut self, f: &T::F, is_leaf: bool) { +impl DynamicArqNode { + fn apply(&mut self, f: &T::F, size: i64) { self.val = T::apply(f, &self.val); - if !is_leaf { + if size > 1 { let h = match self.app { Some(ref g) => T::compose(f, g), None => f.clone(), @@ -40,87 +42,69 @@ impl DynamicArqNode { } } -pub type ArqView = (usize, i64, i64); +pub type ArqView = (usize, i64); /// A dynamic, and optionally persistent, associative range query data structure. pub struct DynamicArq { nodes: Vec>, is_persistent: bool, - initializer: Box T::M>, } impl DynamicArq { /// Initializes the data structure without creating any nodes. - /// The initializer f must satisfy: f(l, r) = T::op(f(l, m), f(m+1, r)) - pub fn new(is_persistent: bool, initializer: Box T::M>) -> Self { + pub fn new(is_persistent: bool) -> Self { Self { nodes: vec![], is_persistent, - initializer, } } - /// Initializes the data structure without creating any nodes. - pub fn new_with_identity(is_persistent: bool) -> Self { - let initializer = Box::new(|_, _| T::identity()); - Self::new(is_persistent, initializer) - } - - /// Lazily builds a tree over the range [l_bound, r_bound]. - pub fn build_using_initializer(&mut self, l_bound: i64, r_bound: i64) -> ArqView { - let view = (self.nodes.len(), l_bound, r_bound); - let val = (self.initializer)(l_bound, r_bound); - self.nodes.push(DynamicArqNode::new(val)); - view + /// Lazily builds a tree initialized to the identity. + pub fn build_from_identity(&mut self, size: i64) -> ArqView { + self.nodes.push(DynamicArqNode::default()); + (self.nodes.len() - 1, size) } /// Builds a tree whose leaves are set to a given non-empty slice. pub fn build_from_slice(&mut self, init_val: &[T::M]) -> ArqView { if init_val.len() == 1 { - let view = (self.nodes.len(), 0, 0); - let val = T::op(&T::identity(), &init_val[0]); - self.nodes.push(DynamicArqNode::new(val)); - return view; + let mut root = DynamicArqNode::default(); + root.val = init_val[0].clone(); + self.nodes.push(root); + (self.nodes.len() - 1, 1) + } else { + let ls = init_val.len() / 2; + let (l_init, r_init) = init_val.split_at(ls); + let l_view = self.build_from_slice(l_init); + let r_view = self.build_from_slice(r_init); + self.merge_equal_sized(l_view, r_view) } - let m = (init_val.len() + 1) / 2; - let (l_init, r_init) = init_val.split_at(m); - let l_view = self.build_from_slice(l_init); - let r_view = self.build_from_slice(r_init); - self.merge_equal_sized(l_view, r_view) } /// Merges two balanced subtrees into a single tree with a 0-indexed view. - pub fn merge_equal_sized(&mut self, l_view: ArqView, r_view: ArqView) -> ArqView { - let l_len = l_view.2 - l_view.1 + 1; - let r_len = r_view.2 - r_view.1 + 1; - assert!(l_len == r_len || l_len == r_len + 1); - - let view = (self.nodes.len(), 0, l_len + r_len - 1); - let root = DynamicArqNode { - val: T::identity(), - app: None, - down: (l_view.0, r_view.0), - }; + pub fn merge_equal_sized(&mut self, (lp, ls): ArqView, (rp, rs): ArqView) -> ArqView { + assert!(ls == rs || ls + 1 == rs); + let p = self.nodes.len(); + let mut root = DynamicArqNode::default(); + root.down = (lp, rp); self.nodes.push(root); - self.pull(view.0); - view + self.pull(p); + (p, ls + rs) } - pub fn push(&mut self, (p, l, r): ArqView) -> (ArqView, ArqView) { - let m = (l + r) / 2; + pub fn push(&mut self, (p, s): ArqView) -> (ArqView, ArqView) { if self.nodes[p].down.0 == usize::max_value() { - let l_val = (self.initializer)(l, m); - let r_val = (self.initializer)(m + 1, r); - self.nodes.push(DynamicArqNode::new(l_val)); - self.nodes.push(DynamicArqNode::new(r_val)); + self.nodes.push(DynamicArqNode::default()); + self.nodes.push(DynamicArqNode::default()); self.nodes[p].down = (self.nodes.len() - 2, self.nodes.len() - 1) }; let (lp, rp) = self.nodes[p].down; + let ls = s / 2; if let Some(ref f) = self.nodes[p].app.take() { - self.nodes[lp].apply(f, l == m); - self.nodes[rp].apply(f, m + 1 == r); + self.nodes[lp].apply(f, ls); + self.nodes[rp].apply(f, s - ls); } - ((lp, l, m), (rp, m + 1, r)) + ((lp, ls), (rp, s - ls)) } fn pull(&mut self, p: usize) { @@ -130,48 +114,50 @@ impl DynamicArq { self.nodes[p].val = T::op(left_val, right_val); } - fn clone_node(&mut self, p: usize) -> usize { + fn clone_node(&mut self, p_orig: usize) -> usize { if self.is_persistent { - let node = self.nodes[p].clone(); + let node = self.nodes[p_orig].clone(); self.nodes.push(node); self.nodes.len() - 1 } else { - p + p_orig } } /// Applies the endomorphism f to all entries from l to r, inclusive. /// If l == r, the updates are eager. Otherwise, they are lazy. pub fn modify(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { - let (p, cl, cr) = view; - if r < cl || cr < l { + let (p_orig, s) = view; + if r < 0 || s - 1 < l { view - } else if l <= cl && cr <= r /* && self.l == self.r forces eager */ { - let p_clone = self.clone_node(p); - self.nodes[p_clone].apply(f, l == r); - (p_clone, cl, cr) + } else if l <= 0 && s - 1 <= r { + let p_clone = self.clone_node(p_orig); + self.nodes[p_clone].apply(f, s); + (p_clone, s) } else { let (l_view, r_view) = self.push(view); - let p_clone = self.clone_node(p); + let ls = l_view.1; + let p_clone = self.clone_node(p_orig); let lp_clone = self.modify(l_view, l, r, f).0; - let rp_clone = self.modify(r_view, l, r, f).0; + let rp_clone = self.modify(r_view, l - ls, r - ls, f).0; self.nodes[p_clone].down = (lp_clone, rp_clone); self.pull(p_clone); - (p_clone, cl, cr) + (p_clone, s) } } /// Returns the aggregate range query on all entries from l to r, inclusive. pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::M { - let (p, cl, cr) = view; - if r < cl || cr < l { + let (p, s) = view; + if r < 0 || s - 1 < l { T::identity() - } else if l <= cl && cr <= r { - T::op(&T::identity(), &self.nodes[p].val) + } else if l <= 0 && s - 1 <= r { + self.nodes[p].val.clone() } else { let (l_view, r_view) = self.push(view); + let ls = l_view.1; let l_agg = self.query(l_view, l, r); - let r_agg = self.query(r_view, l, r); + let r_agg = self.query(r_view, l - ls, r - ls); T::op(&l_agg, &r_agg) } } @@ -180,16 +166,16 @@ impl DynamicArq { /// An example of binary search on the tree of a DynamicArq. /// The tree may have any size, not necessarily a power of two. pub fn first_negative(arq: &mut DynamicArq, view: ArqView) -> Option { - let (p, cl, cr) = view; - if cl == cr { - Some(cl).filter(|_| arq.nodes[p].val < 0) + let (p, s) = view; + if s == 1 { + Some(0).filter(|_| arq.nodes[p].val < 0) } else { let (l_view, r_view) = arq.push(view); - let lp = l_view.0; + let (lp, ls) = l_view; if arq.nodes[lp].val < 0 { first_negative(arq, l_view) } else { - first_negative(arq, r_view) + first_negative(arq, r_view).map(|x| ls + x) } } } diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 09883cf..aceb74c 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -26,9 +26,8 @@ mod test { #[test] fn test_dynamic_rmq() { - let initializer = Box::new(|_, _| 0); - let mut arq = DynamicArq::::new(false, initializer); - let view = arq.build_using_initializer(0, 9); + let mut arq = DynamicArq::::new(false); + let view = arq.build_from_slice(&[0; 10]); assert_eq!(arq.query(view, 0, 9), 0); @@ -41,9 +40,8 @@ mod test { #[test] fn test_persistent_rmq() { - let initializer = Box::new(|_, _| 0); - let mut arq = DynamicArq::::new(true, initializer); - let mut view = arq.build_using_initializer(0, 9); + let mut arq = DynamicArq::::new(true); + let mut view = arq.build_from_slice(&[0; 10]); let at_init = view; view = arq.modify(view, 2, 4, &-5); @@ -56,6 +54,19 @@ mod test { assert_eq!(arq.query(view, 0, 9), -3); } + #[test] + fn test_huge_rmq() { + let quintillion = 1_000_000_000_000_000_000; + let mut arq = DynamicArq::::new(false); + let view = arq.build_from_identity(9 * quintillion + 1); + + arq.modify(view, 2 * quintillion, 4 * quintillion, &-5); + arq.modify(view, 5 * quintillion, 7 * quintillion, &-3); + arq.modify(view, 1 * quintillion, 6 * quintillion, &1); + + assert_eq!(arq.query(view, 0, 9 * quintillion), -3); + } + #[test] fn test_range_sum() { let mut arq = StaticArq::::new(&[(0, 1); 10]); @@ -71,9 +82,8 @@ mod test { #[test] fn test_dynamic_range_sum() { - let initializer = Box::new(|l, r| (0, 1 + r - l)); - let mut arq = DynamicArq::::new(false, initializer); - let view = arq.build_using_initializer(0, 9); + let mut arq = DynamicArq::::new(false); + let view = arq.build_from_slice(&[(0, 1); 10]); assert_eq!(arq.query(view, 0, 9), (0, 10)); @@ -97,8 +107,8 @@ mod test { #[test] fn test_dynamic_supply_demand() { - let mut arq = DynamicArq::::new_with_identity(false); - let view = arq.build_using_initializer(0, 9); + let mut arq = DynamicArq::::new(false); + let view = arq.build_from_identity(10); arq.modify(view, 1, 1, &(25, 100)); arq.modify(view, 3, 3, &(100, 30)); @@ -121,9 +131,9 @@ mod test { } #[test] - fn test_dynslice_binary_search_rmq() { + fn test_dynamic_binary_search_rmq() { let vec = vec![2, 1, 0, -1, -2, -3, -4, -5]; - let mut arq = DynamicArq::::new_with_identity(false); + let mut arq = DynamicArq::::new(false); let view = arq.build_from_slice(&vec); let first_neg = dynamic_arq::first_negative(&mut arq, view); @@ -133,22 +143,4 @@ mod test { assert_eq!(first_neg, Some(3)); assert_eq!(first_neg_zeros, None); } - - #[test] - fn test_dynamic_binary_search_rmq() { - let initializer = Box::new(|_, r| 2 - r); - let (l_bound, r_bound) = (0, 1_000_000_000_000_000_000); - let mut arq = DynamicArq::::new(false, initializer); - let view = arq.build_using_initializer(l_bound, r_bound); - let first_neg = dynamic_arq::first_negative(&mut arq, view); - - arq.modify(view, 2, r_bound - 1, &0); - let first_neg_late = dynamic_arq::first_negative(&mut arq, view); - arq.modify(view, 2, r_bound, &0); - let first_neg_zeros = dynamic_arq::first_negative(&mut arq, view); - - assert_eq!(first_neg, Some(3)); - assert_eq!(first_neg_late, Some(r_bound)); - assert_eq!(first_neg_zeros, None); - } } diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs index b26512b..2337efe 100644 --- a/src/range_query/specs.rs +++ b/src/range_query/specs.rs @@ -7,7 +7,7 @@ pub trait ArqSpec { // their parts. This representation is more efficient. type F: Clone; /// Type of monoid elements. - type M; + type M: Clone; /// For eager updates, compose() ho be unimplemented!(). For lazy updates: /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index 4b23711..ba69a4b 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -23,8 +23,8 @@ impl StaticArq { /// Initializes a static balanced tree on top of the given sequence. pub fn new(init_val: &[T::M]) -> Self { let size = init_val.len(); - let mut val = (0..size).map(|_| T::identity()).collect::>(); - val.extend(init_val.iter().map(|v| T::op(&T::identity(), v))); + let mut val = vec![T::identity(); size]; + val.extend_from_slice(init_val); let app = vec![None; size]; let mut arq = Self { val, app }; From e4fd17de7c77ef6009f0a9fbbe561b4ac08bad46 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 28 Feb 2020 14:00:09 -0800 Subject: [PATCH 38/71] Added Dijkstra, removed BitSet dependency --- .gitignore | 1 + Cargo.toml | 5 +- README.md | 22 ++-- src/graph/connectivity.rs | 3 +- src/graph/dfs.rs | 120 ---------------------- src/graph/mod.rs | 62 ++---------- src/graph/util.rs | 206 ++++++++++++++++++++++++++++++++++++++ src/string_proc.rs | 14 +-- 8 files changed, 239 insertions(+), 194 deletions(-) delete mode 100644 src/graph/dfs.rs create mode 100644 src/graph/util.rs diff --git a/.gitignore b/.gitignore index 4308d82..ea1cfd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ **/*.rs.bk Cargo.lock +.idea/ diff --git a/Cargo.toml b/Cargo.toml index ecc07f1..b3786f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.1.3" +version = "0.1.4" authors = ["Aram Ebtekar"] edition = "2018" @@ -13,6 +13,3 @@ license = "MIT" [badges] travis-ci = { repository = "EbTech/rust-algorithms", branch = "master" } - -[dependencies] -bit-vec = "0.6.*" diff --git a/README.md b/README.md index 4b79873..ef903ad 100644 --- a/README.md +++ b/README.md @@ -15,28 +15,36 @@ In addition, the Rust language has outstanding pedagogical attributes. Its compi ## For Programming Contests -The original intent of this project was to build a reference for use in programming contests ranging from [Codeforces](https://codeforces.com) to [Google's Kick Start and Code Jam](https://codingcompetitions.withgoogle.com), [LeetCode](https://leetcode.com/contest) and [HackerRank](https://www.hackerrank.com/contests). As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. +The original intent of this project was to build a reference for use in programming contests. As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. Most competitive programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! -For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). - -Other online judges that support Rust: +Some contest sites and online judges that support Rust: +- [Codeforces](https://codeforces.com) +- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - [Kattis](https://open.kattis.com/help/rust) -- [Timus](http://acm.timus.ru/help.aspx?topic=rust) - [SPOJ](https://www.spoj.com/) +- [LeetCode](https://leetcode.com/contest) +- [HackerRank](https://www.hackerrank.com/contests) + +The following support pre-2018 versions of Rust: +- [AtCoder](https://atcoder.jp) +- [Timus](http://acm.timus.ru/help.aspx?topic=rust) + +For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). ## Programming Language Advocacy -My other goal is to appeal to developers who feel limited by mainstream, arguably outdated, programming languages. Perhaps we have a better option. +My other goal is to appeal to developers who feel limited by ancient (yet still mainstream) programming languages, by demonstrating the power of modern techniques. Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/) ## Contents -- [Basic graph representations](src/graph/mod.rs): adjacency lists, minimum spanning tree, Euler path, disjoint set union +- [Basic graph representations](src/graph/mod.rs): adjacency lists, disjoint set union +- [Elementary Graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT - [Associative range query](src/range_query): known colloquially as *segtrees*, and Mo's query square root decomposition diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 2cafc72..8404268 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -196,7 +196,8 @@ mod test { #[test] fn test_toposort() { - let mut graph = Graph::new(4, 4); + let mut graph = Graph::new(4, 5); + graph.add_edge(0, 0); graph.add_edge(0, 2); graph.add_edge(3, 2); graph.add_edge(3, 1); diff --git a/src/graph/dfs.rs b/src/graph/dfs.rs deleted file mode 100644 index 6c57aa6..0000000 --- a/src/graph/dfs.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::Graph; -use crate::graph::AdjListIterator; -use bit_vec::BitVec; - -impl Graph { - pub fn dfs(&self, v: usize) -> DfsIterator { - // Create a stack for DFS - let mut stack: Vec = Vec::new(); - - let adj_iters = (0..self.num_v()) - .map(|u| self.adj_list(u)) - .collect::>(); - - // Push the current source node. - stack.push(v); - - DfsIterator { - visited: BitVec::from_elem(self.num_v(), false), - stack, - adj_iters, - } - } -} -pub struct DfsIterator<'a> { - //is vertex visited - visited: BitVec, - //stack of vertices - stack: Vec, - adj_iters: Vec>, -} - -impl<'a> Iterator for DfsIterator<'a> { - type Item = usize; - - /// Returns next vertex in the DFS - fn next(&mut self) -> Option { - //Sources: - // https://www.geeksforgeeks.org/iterative-depth-first-traversal/ - // https://en.wikipedia.org/wiki/Depth-first_search - while let Some(&s) = self.stack.last() { - //Does s still have neighbors we need to process? - if let Some((_, s_nbr)) = self.adj_iters[s].next() { - if !self.visited[s_nbr] { - self.stack.push(s_nbr); - } - } else { - //s has no more neighbors, we can pop it off the stack - self.stack.pop(); - } - - // Stack may contain same vertex twice. So - // we return the popped item only - // if it is not visited. - if !self.visited[s] { - self.visited.set(s, true); - return Some(s); - } - } - - None - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_dfs() { - let mut graph = Graph::new(4, 8); - graph.add_edge(0, 2); - graph.add_edge(2, 0); - graph.add_edge(1, 2); - graph.add_edge(0, 1); - graph.add_edge(3, 3); - graph.add_edge(2, 3); - - let dfs_search = graph.dfs(2).collect::>(); - assert_eq!(dfs_search, vec![2, 3, 0, 1]); - } - - #[test] - fn test_dfs2() { - let mut graph = Graph::new(5, 8); - graph.add_edge(0, 2); - graph.add_edge(2, 1); - graph.add_edge(1, 0); - graph.add_edge(0, 3); - graph.add_edge(3, 4); - graph.add_edge(4, 0); - - let dfs_search = graph.dfs(0).collect::>(); - //Note this is not the only valid DFS - assert_eq!(dfs_search, vec![0, 3, 4, 2, 1]); - } - - #[test] - fn test_dfs_space_complexity() { - let num_v = 20; - let mut graph = Graph::new(num_v, 0); - for i in 0..num_v { - for j in 0..num_v { - graph.add_undirected_edge(i, j); - } - } - - let mut dfs_search = graph.dfs(7); - let mut dfs_check = vec![]; - for _ in 0..num_v { - dfs_check.push(dfs_search.next().unwrap()); - assert!(dfs_search.stack.len() <= num_v + 1); - } - - dfs_check.sort(); - dfs_check.dedup(); - assert_eq!(0, dfs_check[0]); - assert_eq!(num_v, dfs_check.len()); - assert_eq!(num_v - 1, dfs_check[num_v - 1]); - } -} diff --git a/src/graph/mod.rs b/src/graph/mod.rs index d0df224..f72a3c3 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -4,8 +4,8 @@ //! //! All methods will panic if given an out-of-bounds element index. pub mod connectivity; -mod dfs; pub mod flow; +mod util; /// Represents a union of disjoint sets. Each set's elements are arranged in a /// tree, whose root is the set's representative. @@ -103,42 +103,6 @@ impl Graph { next_e: self.first[u], } } - - /// Finds the sequence of edges in an Euler path starting from u, assuming - /// it exists and that the graph is directed. Undefined behavior if this - /// precondition is violated. To extend this to undirected graphs, maintain - /// a visited array to skip the reverse edge. - pub fn euler_path(&self, u: usize) -> Vec { - let mut adj_iters = (0..self.num_v()) - .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); - 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) { - while let Some((e, v)) = adj[u].next() { - self.euler_recurse(v, adj, edges); - edges.push(e); - } - } - - /// Kruskal's minimum spanning tree algorithm on an undirected graph. - pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - assert_eq!(self.num_e(), 2 * weights.len()); - let mut edges = (0..weights.len()).collect::>(); - edges.sort_unstable_by_key(|&e| weights[e]); - - let mut components = DisjointSets::new(self.num_v()); - edges - .into_iter() - .filter(|&e| components.merge(self.endp[2 * e], self.endp[2 * e + 1])) - .collect() - } } /// An iterator for convenient adjacency list traversal. @@ -165,27 +129,15 @@ mod test { use super::*; #[test] - fn test_euler() { - let mut graph = Graph::new(3, 4); + fn test_adj_list() { + let mut graph = Graph::new(4, 4); graph.add_edge(0, 1); - graph.add_edge(1, 0); graph.add_edge(1, 2); - graph.add_edge(2, 1); + graph.add_edge(1, 3); + graph.add_edge(3, 0); - assert_eq!(graph.euler_path(0), vec![0, 2, 3, 1]); - } + let adj: Vec<(usize, usize)> = graph.adj_list(1).collect(); - #[test] - fn test_min_spanning_tree() { - let mut graph = Graph::new(3, 3); - graph.add_undirected_edge(0, 1); - graph.add_undirected_edge(1, 2); - graph.add_undirected_edge(2, 0); - let weights = [7, 3, 5]; - - let mst = graph.min_spanning_tree(&weights); - let mst_cost = mst.iter().map(|&e| weights[e]).sum::(); - assert_eq!(mst, vec![1, 2]); - assert_eq!(mst_cost, 8); + assert_eq!(adj, vec![(2, 3), (1, 2)]); } } diff --git a/src/graph/util.rs b/src/graph/util.rs new file mode 100644 index 0000000..489105a --- /dev/null +++ b/src/graph/util.rs @@ -0,0 +1,206 @@ +use super::{DisjointSets, Graph}; +use crate::graph::AdjListIterator; +use std::cmp::Reverse; + +impl Graph { + /// Finds the sequence of edges in an Euler path starting from u, assuming + /// it exists and that the graph is directed. Undefined behavior if this + /// precondition is violated. To extend this to undirected graphs, maintain + /// a visited array to skip the reverse edge. + pub fn euler_path(&self, u: usize) -> Vec { + let mut adj_iters = (0..self.num_v()) + .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); + 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) { + while let Some((e, v)) = adj[u].next() { + self.euler_recurse(v, adj, edges); + edges.push(e); + } + } + + /// Kruskal's minimum spanning tree algorithm on an undirected graph. + pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { + assert_eq!(self.num_e(), 2 * weights.len()); + let mut edges = (0..weights.len()).collect::>(); + edges.sort_unstable_by_key(|&e| weights[e]); + + let mut components = DisjointSets::new(self.num_v()); + edges + .into_iter() + .filter(|&e| components.merge(self.endp[2 * e], self.endp[2 * e + 1])) + .collect() + } + + // 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 heap = std::collections::BinaryHeap::new(); + + dist[u] = 0; + heap.push((Reverse(0), 0)); + while let Some((Reverse(dist_u), u)) = heap.pop() { + if dist[u] == dist_u { + for (e, v) in self.adj_list(u) { + let dist_v = dist_u + weights[e]; + if dist[v] > dist_v { + dist[v] = dist_v; + heap.push((Reverse(dist_v), v)); + } + } + } + } + dist + } + + pub fn dfs(&self, u: usize) -> DfsIterator { + let adj_iters = (0..self.num_v()) + .map(|u| self.adj_list(u)) + .collect::>(); + + DfsIterator { + visited: vec![false; self.num_v()], + stack: vec![u], + adj_iters, + } + } +} + +pub struct DfsIterator<'a> { + visited: Vec, + stack: Vec, + adj_iters: Vec>, +} + +impl<'a> Iterator for DfsIterator<'a> { + type Item = usize; + + /// Returns next vertex in the DFS + fn next(&mut self) -> Option { + // Sources: + // https://www.geeksforgeeks.org/iterative-depth-first-traversal/ + // https://en.wikipedia.org/wiki/Depth-first_search + while let Some(&u) = self.stack.last() { + if let Some((_, v)) = self.adj_iters[u].next() { + if !self.visited[v] { + self.stack.push(v); + } + } else { + self.stack.pop(); + } + + // Stack may contain same vertex twice. So + // we return the popped item only + // if it is not visited. + if !self.visited[u] { + self.visited[u] = true; + return Some(u); + } + } + + None + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_euler() { + let mut graph = Graph::new(3, 4); + graph.add_edge(0, 1); + graph.add_edge(1, 0); + graph.add_edge(1, 2); + graph.add_edge(2, 1); + + assert_eq!(graph.euler_path(0), vec![0, 2, 3, 1]); + } + + #[test] + fn test_min_spanning_tree() { + let mut graph = Graph::new(3, 6); + graph.add_undirected_edge(0, 1); + graph.add_undirected_edge(1, 2); + graph.add_undirected_edge(2, 0); + let weights = [7, 3, 5]; + + let mst = graph.min_spanning_tree(&weights); + let mst_cost = mst.iter().map(|&e| weights[e]).sum::(); + assert_eq!(mst, vec![1, 2]); + assert_eq!(mst_cost, 8); + } + + #[test] + fn test_dijkstra() { + let mut graph = Graph::new(3, 3); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(2, 0); + let weights = [7, 3, 5]; + + let dist = graph.dijkstra(&weights, 0); + assert_eq!(dist, vec![0, 7, 10]); + } + + #[test] + fn test_dfs() { + let mut graph = Graph::new(4, 8); + graph.add_edge(0, 2); + graph.add_edge(2, 0); + graph.add_edge(1, 2); + graph.add_edge(0, 1); + graph.add_edge(3, 3); + graph.add_edge(2, 3); + + let dfs_search = graph.dfs(2).collect::>(); + assert_eq!(dfs_search, vec![2, 3, 0, 1]); + } + + #[test] + fn test_dfs2() { + let mut graph = Graph::new(5, 8); + graph.add_edge(0, 2); + graph.add_edge(2, 1); + graph.add_edge(1, 0); + graph.add_edge(0, 3); + graph.add_edge(3, 4); + graph.add_edge(4, 0); + + let dfs_search = graph.dfs(0).collect::>(); + //Note this is not the only valid DFS + assert_eq!(dfs_search, vec![0, 3, 4, 2, 1]); + } + + #[test] + fn test_dfs_space_complexity() { + let num_v = 20; + let mut graph = Graph::new(num_v, 0); + for i in 0..num_v { + for j in 0..num_v { + graph.add_undirected_edge(i, j); + } + } + + let mut dfs_search = graph.dfs(7); + let mut dfs_check = vec![]; + for _ in 0..num_v { + dfs_check.push(dfs_search.next().unwrap()); + assert!(dfs_search.stack.len() <= num_v + 1); + } + + dfs_check.sort(); + dfs_check.dedup(); + assert_eq!(0, dfs_check[0]); + assert_eq!(num_v, dfs_check.len()); + assert_eq!(num_v - 1, dfs_check[num_v - 1]); + } +} diff --git a/src/string_proc.rs b/src/string_proc.rs index 8db4b47..8321e84 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -1,4 +1,5 @@ //! String processing algorithms. +use std::cmp::{max, min}; /// Data structure for Knuth-Morris-Pratt string matching against a pattern. pub struct Matcher<'a, T> { @@ -117,12 +118,12 @@ 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, n.max(256)); + sfx = Self::counting_sort(pos, &prev_rank, max(n, 256)); let mut prev = sfx[0]; cur_rank[prev] = 0; for &cur in sfx.iter().skip(1) { - if prev.max(cur) + skip < n + if max(prev, cur) + skip < n && prev_rank[prev] == prev_rank[cur] && prev_rank[prev + skip] == prev_rank[cur + skip] { @@ -145,7 +146,7 @@ impl SuffixArray { i += 1 << k; j += 1 << k; len += 1 << k; - if i.max(j) >= self.sfx.len() { + if max(i, j) >= self.sfx.len() { break; } } @@ -195,16 +196,15 @@ impl Trie { /// /// Panics if text is empty. pub fn palindromes(text: &[T]) -> Vec { - let mut pal = Vec::with_capacity(2 * text.len() - 1); // only mutable var! + let mut pal = Vec::with_capacity(2 * text.len() - 1); pal.push(1); while pal.len() < pal.capacity() { let i = pal.len() - 1; - let max_len = (i + 1).min(pal.capacity() - i); + let max_len = min(i + 1, pal.capacity() - i); while pal[i] < max_len && text[(i - pal[i] - 1) / 2] == text[(i + pal[i] + 1) / 2] { pal[i] += 2; } - if pal[i] < 2 { - let a = 1 - pal[i]; + if let Some(a) = 1usize.checked_sub(pal[i]) { pal.push(a); } else { for d in 1.. { From 4fa179244f189365b50ba972e9cc0d7189852639 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 12 Apr 2020 01:11:59 -0700 Subject: [PATCH 39/71] cleaned up I/O examples --- src/graph/connectivity.rs | 4 ++-- src/scanner.rs | 50 +++++++++++++++++++-------------------- tests/codeforces343d.rs | 27 ++++++++++----------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 8404268..4402e7d 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -17,8 +17,8 @@ impl ConnectivityData { time: 0, vis: vec![0; num_v].into_boxed_slice(), low: vec![0; num_v].into_boxed_slice(), - v_stack: Vec::new(), - e_stack: Vec::new(), + v_stack: vec![], + e_stack: vec![], } } diff --git a/src/scanner.rs b/src/scanner.rs index ae6f5a9..a57b408 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -13,7 +13,7 @@ impl Scanner { pub fn new(reader: R) -> Self { Self { reader, - buffer: Vec::new(), + buffer: vec![], } } @@ -46,7 +46,7 @@ impl UnsafeScanner { pub fn new(reader: R) -> Self { Self { reader, - buf_str: Vec::new(), + buf_str: vec![], buf_iter: "".split_ascii_whitespace(), } } @@ -84,32 +84,36 @@ pub fn writer_to_file(filename: &str) -> io::BufWriter { mod test { use super::*; - #[test] - fn test_in_memory_io() { - let input = "50 8".as_bytes(); - let mut scan = Scanner::new(input); - let mut out = String::new(); - use std::fmt::Write; // needed for writeln!() + fn solve(scan: &mut Scanner, out: &mut W) { + let x = scan.token::(); + let y = scan.token::(); + writeln!(out, "{} - {} = {}", x, y, x - y).ok(); + } + fn unsafe_solve(scan: &mut UnsafeScanner, out: &mut W) { let x = scan.token::(); let y = scan.token::(); - writeln!(out, "Test {}", x - y).ok(); + writeln!(out, "{} - {} = {}", x, y, x - y).ok(); + } + + #[test] + fn test_in_memory_io() { + let input: &[u8] = b"50 8"; + let mut scan = Scanner::new(input); + let mut out = vec![]; - assert_eq!(out, "Test 42\n"); + solve(&mut scan, &mut out); + assert_eq!(out, b"50 - 8 = 42\n"); } #[test] fn test_in_memory_unsafe() { - let input = "50 8".as_bytes(); + let input: &[u8] = b"50 8"; let mut scan = UnsafeScanner::new(input); - let mut out = String::new(); - use std::fmt::Write; // needed for writeln!() - - let x = scan.token::(); - let y = scan.token::(); - writeln!(out, "Test {}", x - y).ok(); + let mut out = vec![]; - assert_eq!(out, "Test 42\n"); + unsafe_solve(&mut scan, &mut out); + assert_eq!(out, b"50 - 8 = 42\n"); } #[test] @@ -117,12 +121,9 @@ mod test { let (stdin, stdout) = (io::stdin(), io::stdout()); let mut scan = Scanner::new(stdin.lock()); let mut out = io::BufWriter::new(stdout.lock()); - use io::Write; // needed for writeln!() if false { - let x = scan.token::(); - let y = scan.token::(); - writeln!(out, "Test {}", x - y).ok(); + solve(&mut scan, &mut out); } } @@ -131,10 +132,7 @@ mod test { fn test_panic_file() { let mut scan = scanner_from_file("input_file.txt"); let mut out = writer_to_file("output_file.txt"); - use io::Write; // needed for writeln!() - let x = scan.token::(); - let y = scan.token::(); - writeln!(out, "Test {}", x - y).ok(); + solve(&mut scan, &mut out); } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index b4f1d94..27cac59 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -6,8 +6,9 @@ extern crate contest_algorithms; use contest_algorithms::graph::Graph; use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; use contest_algorithms::scanner::Scanner; +use std::io; -const SAMPLE_INPUT: &str = "\ +const SAMPLE_INPUT: &[u8] = b"\ 5 1 2 5 1 @@ -27,7 +28,7 @@ const SAMPLE_INPUT: &str = "\ 3 4 3 5 "; -const SAMPLE_OUTPUT: &str = "\ +const SAMPLE_OUTPUT: &[u8] = b"\ 0 0 0 @@ -59,18 +60,7 @@ fn dfs( r[u] = *time; } -#[test] -fn main() { - use std::fmt::Write; - let mut scan = Scanner::new(SAMPLE_INPUT.as_bytes()); - let mut out = String::new(); - /* To read/write with stdin/stdout instead: - use std::io::{self, Write}; - let (stdin, stdout) = (io::stdin(), io::stdout()); - let mut scan = Scanner::new(stdin.lock()); - let mut out = io::BufWriter::new(stdout.lock()); - */ - +fn solve(scan: &mut Scanner, out: &mut W) { let n = scan.token::(); let mut tree = Graph::new(n, 2 * (n - 1)); for _ in 1..n { @@ -102,6 +92,13 @@ fn main() { writeln!(out, "{}", ans).ok(); } } +} + +#[test] +fn main() { + let mut scan = Scanner::new(SAMPLE_INPUT); + let mut out = vec![]; + solve(&mut scan, &mut out); assert_eq!(out, SAMPLE_OUTPUT); -} +} \ No newline at end of file From 468c0b0ce2faca0ce1014f97097b54b5611744e3 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 13 Apr 2020 21:35:10 -0700 Subject: [PATCH 40/71] Made var/type/fn names consistent with blog post and added link to it --- README.md | 2 +- src/range_query/README.md | 3 ++ src/range_query/dynamic_arq.rs | 20 ++++----- src/range_query/mod.rs | 48 ++++++++++----------- src/range_query/specs.rs | 78 +++++++++++++++++----------------- src/range_query/static_arq.rs | 23 +++++----- tests/codeforces343d.rs | 6 +-- 7 files changed, 93 insertions(+), 87 deletions(-) create mode 100644 src/range_query/README.md diff --git a/README.md b/README.md index ef903ad..17d64e6 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ 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) -- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - [Kattis](https://open.kattis.com/help/rust) - [SPOJ](https://www.spoj.com/) - [LeetCode](https://leetcode.com/contest) - [HackerRank](https://www.hackerrank.com/contests) The following support pre-2018 versions of Rust: +- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - [AtCoder](https://atcoder.jp) - [Timus](http://acm.timus.ru/help.aspx?topic=rust) diff --git a/src/range_query/README.md b/src/range_query/README.md new file mode 100644 index 0000000..aeb953a --- /dev/null +++ b/src/range_query/README.md @@ -0,0 +1,3 @@ +# Associative Range Query (ARQ) and Mo's Algorithm + +For more information on Associative Range Query, you may research "segment trees" in the programming contest literature. My implementation is more general than usual; for more information on it, please see my [blog post on Codeforces](https://codeforces.com/blog/entry/68419). diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index 97f279e..f12625b 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -1,9 +1,9 @@ -//! Associative Range Query Tree with dynamic allocation, supporting dynamic -//! node construction and persistence +//! Associative Range Query Tree with dynamic allocation, supporting sparse +//! initialization and persistence use super::ArqSpec; pub struct DynamicArqNode { - val: T::M, + val: T::S, app: Option, down: (usize, usize), } @@ -66,7 +66,7 @@ impl DynamicArq { } /// Builds a tree whose leaves are set to a given non-empty slice. - pub fn build_from_slice(&mut self, init_val: &[T::M]) -> ArqView { + pub fn build_from_slice(&mut self, init_val: &[T::S]) -> ArqView { if init_val.len() == 1 { let mut root = DynamicArqNode::default(); root.val = init_val[0].clone(); @@ -126,7 +126,7 @@ impl DynamicArq { /// Applies the endomorphism f to all entries from l to r, inclusive. /// If l == r, the updates are eager. Otherwise, they are lazy. - pub fn modify(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { + pub fn update(&mut self, view: ArqView, l: i64, r: i64, f: &T::F) -> ArqView { let (p_orig, s) = view; if r < 0 || s - 1 < l { view @@ -138,8 +138,8 @@ impl DynamicArq { let (l_view, r_view) = self.push(view); let ls = l_view.1; let p_clone = self.clone_node(p_orig); - let lp_clone = self.modify(l_view, l, r, f).0; - let rp_clone = self.modify(r_view, l - ls, r - ls, f).0; + let lp_clone = self.update(l_view, l, r, f).0; + let rp_clone = self.update(r_view, l - ls, r - ls, f).0; self.nodes[p_clone].down = (lp_clone, rp_clone); self.pull(p_clone); (p_clone, s) @@ -147,7 +147,7 @@ impl DynamicArq { } /// Returns the aggregate range query on all entries from l to r, inclusive. - pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::M { + pub fn query(&mut self, view: ArqView, l: i64, r: i64) -> T::S { let (p, s) = view; if r < 0 || s - 1 < l { T::identity() @@ -163,8 +163,8 @@ impl DynamicArq { } } -/// An example of binary search on the tree of a DynamicArq. -/// The tree may have any size, not necessarily a power of two. +/// An example of binary search to find the first position whose element is negative. +/// The DynamicArq version works on trees of any size, not necessarily a power of two. pub fn first_negative(arq: &mut DynamicArq, view: ArqView) -> Option { let (p, s) = view; if s == 1 { diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index aceb74c..02993c8 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -17,9 +17,9 @@ mod test { assert_eq!(arq.query(0, 9), 0); - arq.modify(2, 4, &-5); - arq.modify(5, 7, &-3); - arq.modify(1, 6, &1); + arq.update(2, 4, &-5); + arq.update(5, 7, &-3); + arq.update(1, 6, &1); assert_eq!(arq.query(0, 9), -3); } @@ -31,9 +31,9 @@ mod test { assert_eq!(arq.query(view, 0, 9), 0); - arq.modify(view, 2, 4, &-5); - arq.modify(view, 5, 7, &-3); - arq.modify(view, 1, 6, &1); + arq.update(view, 2, 4, &-5); + arq.update(view, 5, 7, &-3); + arq.update(view, 1, 6, &1); assert_eq!(arq.query(view, 0, 9), -3); } @@ -44,10 +44,10 @@ mod test { let mut view = arq.build_from_slice(&[0; 10]); let at_init = view; - view = arq.modify(view, 2, 4, &-5); + view = arq.update(view, 2, 4, &-5); let snapshot = view; - view = arq.modify(view, 5, 7, &-3); - view = arq.modify(view, 1, 6, &1); + view = arq.update(view, 5, 7, &-3); + view = arq.update(view, 1, 6, &1); assert_eq!(arq.query(at_init, 0, 9), 0); assert_eq!(arq.query(snapshot, 0, 9), -5); @@ -60,9 +60,9 @@ mod test { let mut arq = DynamicArq::::new(false); let view = arq.build_from_identity(9 * quintillion + 1); - arq.modify(view, 2 * quintillion, 4 * quintillion, &-5); - arq.modify(view, 5 * quintillion, 7 * quintillion, &-3); - arq.modify(view, 1 * quintillion, 6 * quintillion, &1); + arq.update(view, 2 * quintillion, 4 * quintillion, &-5); + arq.update(view, 5 * quintillion, 7 * quintillion, &-3); + arq.update(view, 1 * quintillion, 6 * quintillion, &1); assert_eq!(arq.query(view, 0, 9 * quintillion), -3); } @@ -73,8 +73,8 @@ mod test { assert_eq!(arq.query(0, 9), (0, 10)); - arq.modify(1, 3, &10); - arq.modify(3, 5, &1); + arq.update(1, 3, &10); + arq.update(3, 5, &1); assert_eq!(arq.query(0, 9), (23, 10)); assert_eq!(arq.query(10, 4), (0, 0)); @@ -87,8 +87,8 @@ mod test { assert_eq!(arq.query(view, 0, 9), (0, 10)); - arq.modify(view, 1, 3, &10); - arq.modify(view, 3, 5, &1); + arq.update(view, 1, 3, &10); + arq.update(view, 3, 5, &1); assert_eq!(arq.query(view, 0, 9), (23, 10)); assert_eq!(arq.query(view, 10, 4), (0, 0)); @@ -98,9 +98,9 @@ mod test { fn test_supply_demand() { let mut arq = StaticArq::::new(&[(0, 0, 0); 10]); - arq.modify(1, 1, &(25, 100)); - arq.modify(3, 3, &(100, 30)); - arq.modify(9, 9, &(0, 20)); + arq.update(1, 1, &(25, 100)); + arq.update(3, 3, &(100, 30)); + arq.update(9, 9, &(0, 20)); assert_eq!(arq.query(0, 9), (125, 150, 75)); } @@ -110,9 +110,9 @@ mod test { let mut arq = DynamicArq::::new(false); let view = arq.build_from_identity(10); - arq.modify(view, 1, 1, &(25, 100)); - arq.modify(view, 3, 3, &(100, 30)); - arq.modify(view, 9, 9, &(0, 20)); + arq.update(view, 1, 1, &(25, 100)); + arq.update(view, 3, 3, &(100, 30)); + arq.update(view, 9, 9, &(0, 20)); assert_eq!(arq.query(view, 0, 9), (125, 150, 75)); } @@ -123,7 +123,7 @@ mod test { let mut arq = StaticArq::::new(&vec); let first_neg = static_arq::first_negative(&mut arq); - arq.modify(3, 7, &0); + arq.update(3, 7, &0); let first_neg_zeros = static_arq::first_negative(&mut arq); assert_eq!(first_neg, Some(3)); @@ -137,7 +137,7 @@ mod test { let view = arq.build_from_slice(&vec); let first_neg = dynamic_arq::first_negative(&mut arq, view); - arq.modify(view, 3, 7, &0); + arq.update(view, 3, 7, &0); let first_neg_zeros = dynamic_arq::first_negative(&mut arq, view); assert_eq!(first_neg, Some(3)); diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs index 2337efe..f85300b 100644 --- a/src/range_query/specs.rs +++ b/src/range_query/specs.rs @@ -1,28 +1,28 @@ //! A collection of example ArqSpec implementations pub trait ArqSpec { + /// Type of underlying array elements. + type S: Clone; /// Type of data representing an endomorphism. - // Note that while a Fn(M) -> M may seem like a more natural representation + // Note that while a Fn(S) -> S may seem like a more natural representation // for an endomorphism, compositions would then have to delegate to each of // their parts. This representation is more efficient. type F: Clone; - /// Type of monoid elements. - type M: Clone; - /// For eager updates, compose() ho be unimplemented!(). For lazy updates: + /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) + fn op(a: &Self::S, b: &Self::S) -> Self::S; + /// Require for all a: op(a, identity()) = op(identity(), a) = a + fn identity() -> Self::S; + /// For eager updates, compose() can be unimplemented!(). For lazy updates: /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) fn compose(f: &Self::F, g: &Self::F) -> Self::F; /// For eager updates, apply() can assume to act on a leaf. For lazy updates: /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) - fn apply(f: &Self::F, a: &Self::M) -> Self::M; - /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) - fn op(a: &Self::M, b: &Self::M) -> Self::M; - /// Require for all a: op(a, identity()) = op(identity(), a) = a - fn identity() -> Self::M; + fn apply(f: &Self::F, a: &Self::S) -> Self::S; } /// Range Minimum Query (RMQ), a classic application of ARQ. -/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// update(l, r, &f) sets all entries a[l..=r] to f. /// query(l, r) finds the minimum value in a[l..=r]. // // Exercises: try augmenting this struct to find the index of a minimum element @@ -31,24 +31,24 @@ pub trait ArqSpec { // try supporting addition: a[i] += f. pub enum AssignMin {} impl ArqSpec for AssignMin { + type S = i64; type F = i64; - type M = i64; + fn op(&a: &Self::S, &b: &Self::S) -> Self::S { + a.min(b) + } + fn identity() -> Self::S { + i64::max_value() + } fn compose(&f: &Self::F, _: &Self::F) -> Self::F { f } - fn apply(&f: &Self::F, _: &Self::M) -> Self::M { + fn apply(&f: &Self::F, _: &Self::S) -> Self::S { f } - fn op(&a: &Self::M, &b: &Self::M) -> Self::M { - a.min(b) - } - fn identity() -> Self::M { - i64::max_value() - } } /// Range Sum Query, a slightly trickier classic application of ARQ. -/// modify(l, r, &f) sets all entries a[l..=r] to f. +/// update(l, r, &f) sets all entries a[l..=r] to f. /// query(l, r) sums all the entries a[l..=r]. /// /// # Panics @@ -57,7 +57,7 @@ impl ArqSpec for AssignMin { // // Note that the apply() operation on raw entries is undefined: while leaf nodes // should simply be set to f, internal nodes must be set to f * size_of_subtree. -// Thus, our monoid type M should store the pair (entry, size_of_subtree). +// Thus, our monoid type S should store the pair (entry, size_of_subtree). // // In mathematical jargon, we say that constant assignment f(a) = f is not an // endomorphism on (i64, +) because f(a+b) = f != 2*f = f(a) + f(b). @@ -66,47 +66,47 @@ impl ArqSpec for AssignMin { // = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). pub enum AssignSum {} impl ArqSpec for AssignSum { + type S = (i64, i64); type F = i64; - type M = (i64, i64); + fn op(&(a, s): &Self::S, &(b, t): &Self::S) -> Self::S { + (a + b, s + t) + } + fn identity() -> Self::S { + (0, 0) + } fn compose(&f: &Self::F, _: &Self::F) -> Self::F { f } - fn apply(&f: &Self::F, &(_, s): &Self::M) -> Self::M { + fn apply(&f: &Self::F, &(_, s): &Self::S) -> Self::S { (f * s, s) } - fn op(&(a, s): &Self::M, &(b, t): &Self::M) -> Self::M { - (a + b, s + t) - } - fn identity() -> Self::M { - (0, 0) - } } /// Supply & Demand, based on https://codeforces.com/gym/102218/problem/F -/// modify(i, i, &(p, o)) increases supply by p and demand by o at time i. +/// update(i, i, &(p, o)) increases supply by p and demand by o at time i. /// query(l, r) computes total supply and demand at times l to r, as well as // how much of the supply is subsequently met by the demand. // // Note that the apply() operation is only correct when applied to leaf nodes. -// Therefore, modify() must only be used in "eager" mode, i.e., with l == r. +// Therefore, update() must only be used in "eager" mode, i.e., with l == r. // compose() should be unimplemented!() to prevent accidental "lazy" updates. pub enum SupplyDemand {} impl ArqSpec for SupplyDemand { + type S = (i64, i64, i64); // production, orders, sales type F = (i64, i64); - type M = (i64, i64, i64); // production, orders, sales + fn op((p1, o1, s1): &Self::S, (p2, o2, s2): &Self::S) -> Self::S { + let extra = (p1 - s1).min(o2 - s2); + (p1 + p2, o1 + o2, s1 + s2 + extra) + } + fn identity() -> Self::S { + (0, 0, 0) + } fn compose(_: &Self::F, _: &Self::F) -> Self::F { unimplemented!() } - fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::M) -> Self::M { + fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::S) -> Self::S { let p = p + p_add; let o = o + o_add; (p, o, p.min(o)) } - fn op((p1, o1, s1): &Self::M, (p2, o2, s2): &Self::M) -> Self::M { - let extra = (p1 - s1).min(o2 - s2); - (p1 + p2, o1 + o2, s1 + s2 + extra) - } - fn identity() -> Self::M { - (0, 0, 0) - } } diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index ba69a4b..a550984 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -1,27 +1,30 @@ -//! Associative Range Query Tree based on [Al.Cash's compact representation] -//! (http://codeforces.com/blog/entry/18051). +//! Associative Range Query Tree use super::ArqSpec; /// Colloquially known as a "segtree" in the sport programming literature, it -/// represents a sequence of elements a_i (0 <= i < size) from a monoid (M, +) +/// represents a sequence of elements a_i (0 <= i < size) from a monoid (S, +) /// on which we want to support fast range operations: /// -/// - modify(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f +/// - update(l, r, f) replaces a_i (l <= i <= r) by f(a_i) for an endomorphism f /// - query(l, r) returns the aggregate a_l + a_{l+1} + ... + a_r /// +/// This compact representation is based on a [blog post by Al.Cash] +/// (http://codeforces.com/blog/entry/18051). All nodes have 0 or 2 children. +/// Hence, trees whose size is not a power of two will have multiple roots. +/// /// Future work: ArqTree would lend itself naturally to Rust's ownership system. /// Initially, we should only have access to the root nodes: /// if size is a power of two, there is a unique root at index 1. /// arq.push(i) locks i and acquires access to its children. /// arq.pull(i) is called when the lock on i is released. pub struct StaticArq { - val: Vec, + val: Vec, app: Vec>, } impl StaticArq { - /// Initializes a static balanced tree on top of the given sequence. - pub fn new(init_val: &[T::M]) -> Self { + /// Initializes a static balanced binary tree on top of the given sequence. + pub fn new(init_val: &[T::S]) -> Self { let size = init_val.len(); let mut val = vec![T::identity(); size]; val.extend_from_slice(init_val); @@ -76,7 +79,7 @@ impl StaticArq { /// # Panics /// /// Panics if r >= size. Note that l > r is valid, meaning an empty range. - pub fn modify(&mut self, mut l: usize, mut r: usize, f: &T::F) { + pub fn update(&mut self, mut l: usize, mut r: usize, f: &T::F) { l += self.app.len(); r += self.app.len(); if l < r { @@ -107,7 +110,7 @@ impl StaticArq { /// # Panics /// /// Panics if r >= size. Note that l > r is valid, meaning an empty range. - pub fn query(&mut self, mut l: usize, mut r: usize) -> T::M { + pub fn query(&mut self, mut l: usize, mut r: usize) -> T::S { l += self.app.len(); r += self.app.len(); if l < r { @@ -131,7 +134,7 @@ impl StaticArq { } } -/// An example of binary search on the tree of a StaticArq. +/// An example of binary search to find the first position whose element is negative. /// In this case, we use RMQ to locate the leftmost negative element. /// To ensure the existence of a valid root note (i == 1) from which to descend, /// the tree's size must be a power of two. diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 27cac59..1c73cd3 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -82,11 +82,11 @@ fn solve(scan: &mut Scanner, out: &mut W) { let (sum, len) = arq.query(l[v], r[v]); if c == 1 { if sum != len { - arq.modify(p[v], p[v], &0); - arq.modify(l[v], r[v], &1); + arq.update(p[v], p[v], &0); + arq.update(l[v], r[v], &1); } } else if c == 2 { - arq.modify(l[v], l[v], &0); + arq.update(l[v], l[v], &0); } else { let ans = if sum == len { 1 } else { 0 }; writeln!(out, "{}", ans).ok(); From 218b095afc91dc684e790b2fa97102444740a04f Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 14 Apr 2020 23:22:13 -0700 Subject: [PATCH 41/71] ArcSpec::apply() takes a size parameter for convenience --- src/range_query/dynamic_arq.rs | 4 ++-- src/range_query/mod.rs | 16 ++++++------- src/range_query/specs.rs | 41 +++++++++++++++++++--------------- src/range_query/static_arq.rs | 20 +++++++++-------- tests/codeforces343d.rs | 7 +++--- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index f12625b..0cc5b90 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -31,7 +31,7 @@ impl Default for DynamicArqNode { impl DynamicArqNode { fn apply(&mut self, f: &T::F, size: i64) { - self.val = T::apply(f, &self.val); + self.val = T::apply(f, &self.val, size); if size > 1 { let h = match self.app { Some(ref g) => T::compose(f, g), @@ -107,7 +107,7 @@ impl DynamicArq { ((lp, ls), (rp, s - ls)) } - fn pull(&mut self, p: usize) { + pub fn pull(&mut self, p: usize) { let (lp, rp) = self.nodes[p].down; let left_val = &self.nodes[lp].val; let right_val = &self.nodes[rp].val; diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 02993c8..332fb59 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -69,29 +69,29 @@ mod test { #[test] fn test_range_sum() { - let mut arq = StaticArq::::new(&[(0, 1); 10]); + let mut arq = StaticArq::::new(&[0; 10]); - assert_eq!(arq.query(0, 9), (0, 10)); + assert_eq!(arq.query(0, 9), 0); arq.update(1, 3, &10); arq.update(3, 5, &1); - assert_eq!(arq.query(0, 9), (23, 10)); - assert_eq!(arq.query(10, 4), (0, 0)); + assert_eq!(arq.query(0, 9), 23); + assert_eq!(arq.query(10, 4), 0); } #[test] fn test_dynamic_range_sum() { let mut arq = DynamicArq::::new(false); - let view = arq.build_from_slice(&[(0, 1); 10]); + let view = arq.build_from_slice(&[0; 10]); - assert_eq!(arq.query(view, 0, 9), (0, 10)); + assert_eq!(arq.query(view, 0, 9), 0); arq.update(view, 1, 3, &10); arq.update(view, 3, 5, &1); - assert_eq!(arq.query(view, 0, 9), (23, 10)); - assert_eq!(arq.query(view, 10, 4), (0, 0)); + assert_eq!(arq.query(view, 0, 9), 23); + assert_eq!(arq.query(view, 10, 4), 0); } #[test] diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs index f85300b..d079079 100644 --- a/src/range_query/specs.rs +++ b/src/range_query/specs.rs @@ -9,16 +9,21 @@ pub trait ArqSpec { // their parts. This representation is more efficient. type F: Clone; - /// Require for all a,b,c: op(a, op(b, c)) = op(op(a, b), c) + /// Must satisfy the Associative Law: + /// For all a,b,c, op(a, op(b, c)) = op(op(a, b), c) fn op(a: &Self::S, b: &Self::S) -> Self::S; - /// Require for all a: op(a, identity()) = op(identity(), a) = a + /// Must satisfy the Identity Law: + /// For all a, op(a, identity()) = op(identity(), a) = a fn identity() -> Self::S; - /// For eager updates, compose() can be unimplemented!(). For lazy updates: - /// Require for all f,g,a: apply(compose(f, g), a) = apply(f, apply(g, a)) + /// For point query / eager updates, compose() can be unimplemented!() + /// For range query / lazy updates, it must satisfy the Composition Law: + /// For all f,g,a, apply(compose(f, g), a) = apply(f, apply(g, a)) fn compose(f: &Self::F, g: &Self::F) -> Self::F; - /// For eager updates, apply() can assume to act on a leaf. For lazy updates: - /// Require for all f,a,b: apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) - fn apply(f: &Self::F, a: &Self::S) -> Self::S; + /// For point query / eager updates, apply() can assume it acts on a leaf. + /// For range query / lazy updates, it must satisfy the Distributive Law: + /// For all f,a,b, apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) + /// The `size` parameter makes this law easier to satisfy in certain cases. + fn apply(f: &Self::F, a: &Self::S, size: i64) -> Self::S; } /// Range Minimum Query (RMQ), a classic application of ARQ. @@ -42,7 +47,7 @@ impl ArqSpec for AssignMin { fn compose(&f: &Self::F, _: &Self::F) -> Self::F { f } - fn apply(&f: &Self::F, _: &Self::S) -> Self::S { + fn apply(&f: &Self::F, _: &Self::S, _: i64) -> Self::S { f } } @@ -55,9 +60,9 @@ impl ArqSpec for AssignMin { /// /// Associated functions will panic on overflow. // -// Note that the apply() operation on raw entries is undefined: while leaf nodes -// should simply be set to f, internal nodes must be set to f * size_of_subtree. -// Thus, our monoid type S should store the pair (entry, size_of_subtree). +// Note that while the `size` parameter seems necessary to satisfy the +// Distributive Law, it is merely a convenience: in essence what we've done +// is move to the product monoid of tuples (value, size_of_subtree). // // In mathematical jargon, we say that constant assignment f(a) = f is not an // endomorphism on (i64, +) because f(a+b) = f != 2*f = f(a) + f(b). @@ -66,19 +71,19 @@ impl ArqSpec for AssignMin { // = (f*s, s) + (f*t, t) = f((a,s)) + f((b,t)). pub enum AssignSum {} impl ArqSpec for AssignSum { - type S = (i64, i64); + type S = i64; type F = i64; - fn op(&(a, s): &Self::S, &(b, t): &Self::S) -> Self::S { - (a + b, s + t) + fn op(&a: &Self::S, &b: &Self::S) -> Self::S { + a + b } fn identity() -> Self::S { - (0, 0) + 0 } fn compose(&f: &Self::F, _: &Self::F) -> Self::F { f } - fn apply(&f: &Self::F, &(_, s): &Self::S) -> Self::S { - (f * s, s) + fn apply(&f: &Self::F, _: &Self::S, size: i64) -> Self::S { + f * size } } @@ -104,7 +109,7 @@ impl ArqSpec for SupplyDemand { fn compose(_: &Self::F, _: &Self::F) -> Self::F { unimplemented!() } - fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::S) -> Self::S { + fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::S, _: i64) -> Self::S { let p = p + p_add; let o = o + o_add; (p, o, p.min(o)) diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index a550984..14a9331 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -37,8 +37,8 @@ impl StaticArq { arq } - fn apply(&mut self, p: usize, f: &T::F) { - self.val[p] = T::apply(f, &self.val[p]); + fn apply(&mut self, p: usize, f: &T::F, s: i64) { + self.val[p] = T::apply(f, &self.val[p], s); if let Some(lazy) = self.app.get_mut(p) { let h = match *lazy { Some(ref g) => T::compose(f, g), @@ -50,8 +50,9 @@ impl StaticArq { fn push(&mut self, p: usize) { if let Some(ref f) = self.app[p].take() { - self.apply(p << 1, f); - self.apply(p << 1 | 1, f); + let s = ((self.app.len() + p - 1) / p / 2).next_power_of_two() as i64; + self.apply(p << 1, f, s); + self.apply(p << 1 | 1, f, s); } } @@ -61,8 +62,8 @@ impl StaticArq { fn push_to(&mut self, p: usize) { let one_plus_floor_log_p = (p + 1).next_power_of_two().trailing_zeros(); - for s in (1..one_plus_floor_log_p).rev() { - self.push(p >> s); + for i in (1..one_plus_floor_log_p).rev() { + self.push(p >> i); } } @@ -86,20 +87,21 @@ impl StaticArq { self.push_to(l); } self.push_to(r); - let (mut l0, mut r0) = (1, 1); + let (mut l0, mut r0, mut s) = (1, 1, 1); while l <= r { if l & 1 == 1 { - self.apply(l, f); + self.apply(l, f, s); l0 = l0.max(l); l += 1; } if r & 1 == 0 { - self.apply(r, f); + self.apply(r, f, s); r0 = r0.max(r); r -= 1; } l >>= 1; r >>= 1; + s <<= 1; } self.pull_from(l0); self.pull_from(r0); diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 1c73cd3..94b911a 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -74,12 +74,13 @@ fn solve(scan: &mut Scanner, out: &mut W) { let mut p = vec![0; n]; dfs(&tree, 0, &mut l, &mut r, &mut p, &mut 0); - let mut arq = StaticArq::::new(&vec![(0, 1); n + 1]); + let mut arq = StaticArq::::new(&vec![0; n + 1]); let q = scan.token::(); for _ in 0..q { let c = scan.token::(); let v = scan.token::() - 1; - let (sum, len) = arq.query(l[v], r[v]); + let len = (r[v] - l[v] + 1) as i64; + let sum = arq.query(l[v], r[v]); if c == 1 { if sum != len { arq.update(p[v], p[v], &0); @@ -101,4 +102,4 @@ fn main() { solve(&mut scan, &mut out); assert_eq!(out, SAMPLE_OUTPUT); -} \ No newline at end of file +} From ad5720e6831c7f53263dd8f57254e6d5fba14990 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 10 Jul 2020 01:16:54 -0700 Subject: [PATCH 42/71] Add Aho-Corasick, make trie more bare-bones --- README.md | 4 +- src/string_proc.rs | 227 ++++++++++++++++++++++++++++++++------------- 2 files changed, 165 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 17d64e6..79eb794 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. -This repository is distributed under the [MIT License](LICENSE). The license text need not be included in contest submissions, though I would appreciate linking back to this repo for others to find. Enjoy! +This repository is distributed under the [MIT License](LICENSE). Contest submissions need not include the license text. Enjoy! ## For Students and Educators @@ -52,4 +52,4 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data ergonomically -- [String processing](src/string_proc.rs): Knuth-Morris-Pratt string matching, suffix arrays, Manacher's palindrome search +- [String processing](src/string_proc.rs): Knuth-Morris-Pratt and Aho-Corasick string matching, suffix array, Manacher's linear-time palindrome search diff --git a/src/string_proc.rs b/src/string_proc.rs index 8321e84..b466d9c 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -1,16 +1,60 @@ //! String processing algorithms. use std::cmp::{max, min}; +use std::collections::{hash_map::Entry, HashMap, VecDeque}; -/// Data structure for Knuth-Morris-Pratt string matching against a pattern. -pub struct Matcher<'a, T> { +/// Prefix trie, easily augmentable by adding more fields and/or methods +pub struct Trie { + links: Vec>, +} + +impl Default for Trie { + /// Creates an empty trie with a root node. + fn default() -> Self { + Self { + links: vec![HashMap::new()], + } + } +} + +impl Trie { + /// Inserts a word into the trie, and returns the index of its node. + pub fn insert(&mut self, word: impl IntoIterator) -> usize { + let mut node = 0; + + for ch in word { + let len = self.links.len(); + node = match self.links[node].entry(ch) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + entry.insert(len); + self.links.push(HashMap::new()); + len + } + } + } + node + } + + /// Finds a word in the trie, and returns the index of its node. + pub fn get(&self, word: impl IntoIterator) -> Option { + let mut node = 0; + for ch in word { + node = *self.links[node].get(&ch)?; + } + Some(node) + } +} + +/// Single-pattern matching with the Knuth-Morris-Pratt algorithm +pub struct Matcher<'a, C: Eq> { /// The string pattern to search for. - pub pattern: &'a [T], + pub pattern: &'a [C], /// KMP match failure automaton. fail[i] is the length of the longest - /// proper prefix-suffix of pattern[0...i]. + /// proper prefix-suffix of pattern[0..=i]. pub fail: Vec, } -impl<'a, T: Eq> Matcher<'a, T> { +impl<'a, C: Eq> Matcher<'a, C> { /// Precomputes the automaton that allows linear-time string matching. /// /// # Example @@ -33,7 +77,7 @@ impl<'a, T: Eq> Matcher<'a, T> { /// # Panics /// /// Panics if pattern is empty. - pub fn new(pattern: &'a [T]) -> Self { + pub fn new(pattern: &'a [C]) -> Self { let mut fail = Vec::with_capacity(pattern.len()); fail.push(0); let mut len = 0; @@ -49,10 +93,10 @@ impl<'a, T: Eq> Matcher<'a, T> { Self { pattern, fail } } - /// KMP algorithm, sets matches[i] = length of longest prefix of pattern - /// matching a suffix of text[0...i]. - pub fn kmp_match(&self, text: &[T]) -> Vec { - let mut matches = Vec::with_capacity(text.len()); + /// KMP algorithm, sets match_lens[i] = length of longest prefix of pattern + /// matching a suffix of text[0..=i]. + pub fn kmp_match(&self, text: &[C]) -> Vec { + let mut match_lens = Vec::with_capacity(text.len()); let mut len = 0; for ch in text { if len == self.pattern.len() { @@ -64,9 +108,94 @@ impl<'a, T: Eq> Matcher<'a, T> { if self.pattern[len] == *ch { len += 1; } - matches.push(len); + match_lens.push(len); } - matches + match_lens + } +} + +/// Multi-pattern matching with the Aho-Corasick algorithm +pub struct MultiMatcher { + /// A prefix trie storing the string patterns to search for. + pub trie: Trie, + /// Stores which completed pattern string each node corresponds to. + pub pat_id: Vec>, + /// Aho-Corasick failure automaton. fail[i] is the node corresponding to the + /// longest prefix-suffix of the node corresponding to i. + pub fail: Vec, + /// Shortcut to the next match along the failure chain, or to the root. + pub fast: Vec, +} + +impl MultiMatcher { + fn next(trie: &Trie, fail: &[usize], mut node: usize, ch: &C) -> usize { + loop { + if let Some(&child) = trie.links[node].get(ch) { + return child; + } else if node == 0 { + return 0; + } + node = fail[node]; + } + } + + /// Precomputes the automaton that allows linear-time string matching. + /// If there are duplicate patterns, all but one copy will be ignored. + pub fn new(patterns: Vec>) -> Self { + let mut trie = Trie::default(); + let pat_nodes: Vec = patterns.into_iter().map(|pat| trie.insert(pat)).collect(); + + let mut pat_id = vec![None; trie.links.len()]; + for (i, node) in pat_nodes.into_iter().enumerate() { + pat_id[node] = Some(i); + } + + let mut fail = vec![0; trie.links.len()]; + let mut fast = vec![0; trie.links.len()]; + let mut q: VecDeque = trie.links[0].values().cloned().collect(); + + while let Some(node) = q.pop_front() { + for (ch, &child) in &trie.links[node] { + 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); + } + } + + Self { + trie, + pat_id, + fail, + fast, + } + } + + /// Aho-Corasick algorithm, sets match_nodes[i] = node corresponding to + /// longest prefix of some pattern matching a suffix of text[0..=i]. + pub fn ac_match(&self, text: &[C]) -> Vec { + let mut match_nodes = Vec::with_capacity(text.len()); + let mut node = 0; + for ch in text { + node = Self::next(&self.trie, &self.fail, node, &ch); + match_nodes.push(node); + } + match_nodes + } + + /// For each non-empty match, returns where in the text it ends, and the index + /// of the corresponding pattern. + pub fn get_end_pos_and_pat_id(&self, match_nodes: &[usize]) -> Vec<(usize, usize)> { + let mut res = vec![]; + for (text_pos, &(mut node)) in match_nodes.iter().enumerate() { + while node != 0 { + if let Some(id) = self.pat_id[node] { + res.push((text_pos + 1, id)); + } + node = self.fast[node]; + } + } + res } } @@ -155,39 +284,6 @@ impl SuffixArray { } } -/// Prefix trie -#[derive(Default)] -pub struct Trie { - count: usize, - branches: std::collections::HashMap>, -} - -impl Trie { - /// Inserts a word into the trie. - pub fn insert(&mut self, word: impl IntoIterator) { - let mut node = self; - node.count += 1; - - for ch in word { - node = { node }.branches.entry(ch).or_default(); - node.count += 1; - } - } - - /// Computes the number of inserted words that start with the given prefix. - pub fn get(&self, prefix: impl IntoIterator) -> usize { - let mut node = self; - - for ch in prefix { - match node.branches.get(&ch) { - Some(sub) => node = sub, - None => return 0, - } - } - node.count - } -} - /// Manacher's algorithm for computing palindrome substrings in linear time. /// pal[2*i] = odd length of palindrome centred at text[i]. /// pal[2*i+1] = even length of palindrome centred at text[i+0.5]. @@ -226,7 +322,7 @@ mod test { use super::*; #[test] - fn test_kmp() { + fn test_kmp_matching() { let text = b"banana"; let pattern = b"ana"; @@ -235,6 +331,27 @@ mod test { assert_eq!(matches, vec![0, 1, 2, 3, 2, 3]); } + #[test] + fn test_ac_matching() { + let text = b"banana bans, apple benefits."; + let dict = vec![ + "banana".bytes(), + "benefit".bytes(), + "banapple".bytes(), + "ban".bytes(), + "fit".bytes(), + ]; + + let matcher = MultiMatcher::new(dict); + let match_nodes = matcher.ac_match(text); + let end_pos_and_id = matcher.get_end_pos_and_pat_id(&match_nodes); + + assert_eq!( + end_pos_and_id, + vec![(3, 3), (6, 0), (10, 3), (26, 1), (26, 4)] + ); + } + #[test] fn test_suffix_array() { let text1 = b"bobocel"; @@ -258,24 +375,6 @@ mod test { } } - #[test] - fn test_trie() { - let dict = vec!["banana", "benefit", "banapple", "ban"]; - - let trie = dict.into_iter().fold(Trie::default(), |mut trie, word| { - Trie::insert(&mut trie, word.bytes()); - trie - }); - - assert_eq!(trie.get("".bytes()), 4); - assert_eq!(trie.get("b".bytes()), 4); - assert_eq!(trie.get("ba".bytes()), 3); - assert_eq!(trie.get("ban".bytes()), 3); - assert_eq!(trie.get("bana".bytes()), 2); - assert_eq!(trie.get("banan".bytes()), 1); - assert_eq!(trie.get("bane".bytes()), 0); - } - #[test] fn test_palindrome() { let text = b"banana"; From 2210400c193b04a4d8d030931d282c1c29a0b2da Mon Sep 17 00:00:00 2001 From: JaroPaska <33621399+JaroPaska@users.noreply.github.com> Date: Sat, 25 Jul 2020 20:18:09 +0200 Subject: [PATCH 43/71] Miller rabin (#13) JaroPaska's prime factoring contributions: Miller-Rabin, Pollard's rho --- .gitignore | 2 + src/math/mod.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/.gitignore b/.gitignore index ea1cfd9..570dcb0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ target/ **/*.rs.bk Cargo.lock .idea/ +.vscode/ +*.code-workspace \ No newline at end of file diff --git a/src/math/mod.rs b/src/math/mod.rs index d53d33f..e17f4fe 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -29,6 +29,108 @@ pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> { } } +fn pos_mod(n: i64, m: i64) -> i64 { + 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) +} + +/// Assuming m >= 1 and exp >= 0, finds base ^ exp % m in logarithmic time +fn mod_exp(mut base: i64, mut exp: i64, m: i64) -> i64 { + assert!(m >= 1); + assert!(exp >= 0); + let mut ans = 1 % m; + base = base % m; + while exp > 0 { + if exp % 2 == 1 { + ans = mod_mul(ans, base, m); + } + base = mod_mul(base, base, m); + exp /= 2; + } + pos_mod(ans, m) +} + +fn is_strong_probable_prime(n: i64, d: i64, r: i64, a: i64) -> bool { + let mut x = mod_exp(a, d, n); + if x == 1 || x == n - 1 { + return true; + } + for _ in 0..(r - 1) { + x = mod_mul(x, x, n); + if x == n - 1 { + return true; + } + } + false +} + +const BASES: [i64; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]; +/// Assuming x >= 0, returns true if x is prime +pub fn is_prime(n: i64) -> bool { + assert!(n >= 0); + match n { + 0 | 1 => return false, + 2 | 3 => return true, + _ if n % 2 == 0 => return false, + _ => {} + }; + let r = (n - 1).trailing_zeros() as i64; + let d = (n - 1) >> r; + BASES + .iter() + .all(|&base| base > n - 2 || is_strong_probable_prime(n, d, r, base)) +} + +fn pollard_rho(n: i64) -> i64 { + for a in 1..n { + let f = |x| pos_mod(mod_mul(x, x, n) + a, n); + let mut x = 2; + let mut y = 2; + loop { + x = f(x); + y = f(f(y)); + let p = num::fast_gcd(x - y, n); + if p > 1 { + if p == n { + break; + } else { + return p; + } + } + } + } + panic!("No divisor found!"); +} + +/// Assuming x >= 1, finds the prime factorization of n +pub fn factorize(n: i64) -> Vec { + assert!(n >= 1); + let r = n.trailing_zeros(); + let mut factors = vec![2; r as usize]; + let mut stack = match n >> r { + 1 => Vec::new(), + x => vec![x] + }; + while let Some(top) = stack.pop() { + if is_prime(top) { + factors.push(top); + } else { + let div = pollard_rho(top); + stack.push(div); + stack.push(top / div); + } + } + factors.sort_unstable(); + factors +} + #[cfg(test)] mod test { use super::*; @@ -44,4 +146,38 @@ mod test { assert_eq!(canon_egcd(a, b, d), Some((d, -2, 1))); assert_eq!(canon_egcd(b, a, d), Some((d, -1, 3))); } + + #[test] + fn test_modexp() { + let m = 1_000_000_007; + assert_eq!(mod_exp(0, 0, m), 1); + assert_eq!(mod_exp(0, 1, m), 0); + assert_eq!(mod_exp(0, 10, m), 0); + assert_eq!(mod_exp(123, 456, m), 565291922); + } + + #[test] + fn test_miller() { + assert_eq!(is_prime(2), true); + assert_eq!(is_prime(4), false); + assert_eq!(is_prime(6), false); + assert_eq!(is_prime(8), false); + assert_eq!(is_prime(269), true); + assert_eq!(is_prime(1000), false); + assert_eq!(is_prime(1_000_000_007), true); + assert_eq!(is_prime((1 << 61) - 1), true); + assert_eq!(is_prime(7156857700403137441), false); + } + + #[test] + fn test_pollard() { + assert_eq!(factorize(1), Vec::new()); + assert_eq!(factorize(2), vec![2]); + assert_eq!(factorize(4), vec![2, 2]); + assert_eq!(factorize(12), vec![2, 2, 3]); + assert_eq!( + factorize(7156857700403137441), + vec![11, 13, 17, 19, 29, 37, 41, 43, 61, 97, 109, 127] + ); + } } From fb0a9ed9b9a636fa218b26cc81aa70b75d410d06 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sat, 25 Jul 2020 12:04:10 -0700 Subject: [PATCH 44/71] small fixes to pass clippy, trie tests, v0.2 --- .travis.yml | 2 +- Cargo.toml | 2 +- src/math/mod.rs | 63 ++++++++++++++++++++++------------------------ src/string_proc.rs | 16 ++++++++++++ 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 41032e8..348c6ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.35.0 # Version currently supported by Codeforces + - 1.42.0 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/Cargo.toml b/Cargo.toml index b3786f3..67f80ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.1.4" +version = "0.2.0" authors = ["Aram Ebtekar"] edition = "2018" diff --git a/src/math/mod.rs b/src/math/mod.rs index e17f4fe..42127cc 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -29,6 +29,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 @@ -36,17 +37,13 @@ fn pos_mod(n: i64, m: i64) -> i64 { 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) } - -/// Assuming m >= 1 and exp >= 0, finds base ^ exp % m in logarithmic time -fn mod_exp(mut base: i64, mut exp: i64, m: i64) -> i64 { +fn mod_exp(mut base: i64, mut exp: u64, m: i64) -> i64 { assert!(m >= 1); - assert!(exp >= 0); let mut ans = 1 % m; - base = base % m; + base %= m; while exp > 0 { if exp % 2 == 1 { ans = mod_mul(ans, base, m); @@ -57,12 +54,12 @@ fn mod_exp(mut base: i64, mut exp: i64, m: i64) -> i64 { pos_mod(ans, m) } -fn is_strong_probable_prime(n: i64, d: i64, r: i64, a: i64) -> bool { - let mut x = mod_exp(a, d, n); +fn is_strong_probable_prime(n: i64, exp: u64, r: i64, a: i64) -> bool { + let mut x = mod_exp(a, exp, n); if x == 1 || x == n - 1 { return true; } - for _ in 0..(r - 1) { + for _ in 1..r { x = mod_mul(x, x, n); if x == n - 1 { return true; @@ -71,21 +68,22 @@ fn is_strong_probable_prime(n: i64, d: i64, r: i64, a: i64) -> bool { false } -const BASES: [i64; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]; -/// Assuming x >= 0, returns true if x is prime +/// Assuming x >= 0, returns whether x is prime pub fn is_prime(n: i64) -> bool { + const BASES: [i64; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]; assert!(n >= 0); match n { - 0 | 1 => return false, - 2 | 3 => return true, - _ if n % 2 == 0 => return false, - _ => {} - }; - let r = (n - 1).trailing_zeros() as i64; - let d = (n - 1) >> r; - BASES - .iter() - .all(|&base| base > n - 2 || is_strong_probable_prime(n, d, r, base)) + 0 | 1 => false, + 2 | 3 => true, + _ if n % 2 == 0 => false, + _ => { + let r = (n - 1).trailing_zeros() as i64; + let exp = (n - 1) as u64 >> r; + BASES + .iter() + .all(|&base| base > n - 2 || is_strong_probable_prime(n, exp, r, base)) + } + } } fn pollard_rho(n: i64) -> i64 { @@ -96,13 +94,11 @@ fn pollard_rho(n: i64) -> i64 { loop { x = f(x); y = f(f(y)); - let p = num::fast_gcd(x - y, n); - if p > 1 { - if p == n { - break; - } else { - return p; - } + let div = num::fast_gcd(x - y, n); + if div == n { + break; + } else if div > 1 { + return div; } } } @@ -110,13 +106,14 @@ fn pollard_rho(n: i64) -> i64 { } /// Assuming x >= 1, finds the prime factorization of n +/// TODO: pollard_rho needs randomization to ensure correctness in contest settings! pub fn factorize(n: i64) -> Vec { assert!(n >= 1); - let r = n.trailing_zeros(); - let mut factors = vec![2; r as usize]; + let r = n.trailing_zeros() as usize; + let mut factors = vec![2; r]; let mut stack = match n >> r { - 1 => Vec::new(), - x => vec![x] + 1 => vec![], + x => vec![x], }; while let Some(top) = stack.pop() { if is_prime(top) { @@ -171,7 +168,7 @@ mod test { #[test] fn test_pollard() { - assert_eq!(factorize(1), Vec::new()); + assert_eq!(factorize(1), vec![]); assert_eq!(factorize(2), vec![2]); assert_eq!(factorize(4), vec![2, 2]); assert_eq!(factorize(12), vec![2, 2, 3]); diff --git a/src/string_proc.rs b/src/string_proc.rs index b466d9c..da3348e 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -321,6 +321,22 @@ pub fn palindromes(text: &[T]) -> Vec { mod test { use super::*; + #[test] + fn test_trie() { + let dict = vec!["banana", "benefit", "banapple", "ban"]; + + let trie = dict.into_iter().fold(Trie::default(), |mut trie, word| { + trie.insert(word.bytes()); + trie + }); + + assert_eq!(trie.get("".bytes()), Some(0)); + assert_eq!(trie.get("b".bytes()), Some(1)); + assert_eq!(trie.get("banana".bytes()), Some(6)); + assert_eq!(trie.get("be".bytes()), Some(7)); + assert_eq!(trie.get("bane".bytes()), None); + } + #[test] fn test_kmp_matching() { let text = b"banana"; From f5c35436d0d8cb8c8f8a2fde9dcf45ac64a8886d Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 16 Aug 2020 16:17:43 -0700 Subject: [PATCH 45/71] coordinate compression and ArqSpec documentation on point queries --- README.md | 6 +++--- src/range_query/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++++ src/range_query/specs.rs | 21 ++++++++++++++------ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 79eb794..0bf9d50 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,11 @@ Rather than try to persuade you with words, this repository aims to show by exam ## Contents - [Basic graph representations](src/graph/mod.rs): adjacency lists, disjoint set union -- [Elementary Graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration +- [Elementary graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/range_query): known colloquially as *segtrees*, and Mo's query square root decomposition -- [GCD Math](src/math/mod.rs): canonical solution to Bezout's identity +- [Associative range query](src/range_query): known colloquially as *segtrees*, coordinate compression, and Mo's query square root decomposition +- [Number thery](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data ergonomically diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 332fb59..560b635 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -6,11 +6,53 @@ pub use dynamic_arq::{ArqView, DynamicArq}; pub use specs::ArqSpec; pub use static_arq::StaticArq; +/// A simple data structure for coordinate compression +pub struct SparseIndex { + coords: Vec +} + +impl SparseIndex { + /// Build an index, given the full set of coordinates to compress. + pub fn new(mut coords: Vec) -> Self { + coords.sort_unstable(); + coords.dedup(); + Self { coords } + } + + /// Return Ok(i) if the coordinate q appears at index i + /// Return Err(i) if q appears between indices i-1 and i + pub fn compress(&self, q: i64) -> Result { + self.coords.binary_search(&q) + } +} + #[cfg(test)] mod test { use super::specs::*; use super::*; + #[test] + fn test_coord_compress() { + let mut coords = vec![16, 99, 45, 18]; + let index = SparseIndex::new(coords.clone()); + + coords.sort_unstable(); + for (i, q) in coords.into_iter().enumerate() { + assert_eq!(index.compress(q - 1), Err(i)); + assert_eq!(index.compress(q), Ok(i)); + assert_eq!(index.compress(q + 1), Err(i + 1)); + } + } + + #[test] + fn test_range_compress() { + let queries = vec![(0, 10), (10, 19), (20, 29)]; + let coords = queries.iter().flat_map(|&(i, j)| vec![i, j + 1]).collect(); + let index = SparseIndex::new(coords); + + assert_eq!(index.coords, vec![0, 10, 11, 20, 30]); + } + #[test] fn test_rmq() { let mut arq = StaticArq::::new(&[0; 10]); diff --git a/src/range_query/specs.rs b/src/range_query/specs.rs index d079079..1340a4c 100644 --- a/src/range_query/specs.rs +++ b/src/range_query/specs.rs @@ -15,15 +15,23 @@ pub trait ArqSpec { /// Must satisfy the Identity Law: /// For all a, op(a, identity()) = op(identity(), a) = a fn identity() -> Self::S; - /// For point query / eager updates, compose() can be unimplemented!() - /// For range query / lazy updates, it must satisfy the Composition Law: + /// Must satisfy the Composition Law: /// For all f,g,a, apply(compose(f, g), a) = apply(f, apply(g, a)) fn compose(f: &Self::F, g: &Self::F) -> Self::F; - /// For point query / eager updates, apply() can assume it acts on a leaf. - /// For range query / lazy updates, it must satisfy the Distributive Law: - /// For all f,a,b, apply(f, op(a, b)) = op(apply(f, a), apply(f, b)) + /// Must satisfy the Distributive Law: + /// For all f,a,b, apply(f, op(a, b), s+t) = op(apply(f, a, s), apply(f, b, t)) /// The `size` parameter makes this law easier to satisfy in certain cases. fn apply(f: &Self::F, a: &Self::S, size: i64) -> Self::S; + + // The following relaxations to the laws may apply. + // If only point updates are made, the Composition and Distributive Laws + // no longer apply. + // - compose() is never called, so it can be left unimplemented!(). + // - apply() is only ever called on leaves, i.e., with size == 1. + // If only point queries are made, the Associative and Distributive Laws + // no longer apply. + // - op()'s result only matters when identity() is an argument. + // - apply()'s result only matters on leaves, i.e., with size == 1. } /// Range Minimum Query (RMQ), a classic application of ARQ. @@ -109,7 +117,8 @@ impl ArqSpec for SupplyDemand { fn compose(_: &Self::F, _: &Self::F) -> Self::F { unimplemented!() } - fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::S, _: i64) -> Self::S { + fn apply(&(p_add, o_add): &Self::F, &(p, o, _): &Self::S, s: i64) -> Self::S { + assert_eq!(s, 1); let p = p + p_add; let o = o + o_add; (p, o, p.min(o)) From 0421fb0a66ef1ced0abffb46f736caf1ac055d25 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 16 Aug 2020 20:11:07 -0700 Subject: [PATCH 46/71] Added upper/lower_bound on slices --- src/range_query/mod.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 560b635..57155c3 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -6,9 +6,25 @@ pub use dynamic_arq::{ArqView, DynamicArq}; pub use specs::ArqSpec; pub use static_arq::StaticArq; +/// Assuming slice is sorted, returns the minimum i for which slice[i] >= key, +/// or slice.len() if no such i exists +pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { + slice + .binary_search_by(|x| x.cmp(key).then(std::cmp::Ordering::Greater)) + .unwrap_err() +} + +/// Assuming slice is sorted, returns the minimum i for which slice[i] > key, +/// or slice.len() if no such i exists +pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { + slice + .binary_search_by(|x| x.cmp(key).then(std::cmp::Ordering::Less)) + .unwrap_err() +} + /// A simple data structure for coordinate compression pub struct SparseIndex { - coords: Vec + coords: Vec, } impl SparseIndex { @@ -31,6 +47,22 @@ mod test { use super::specs::*; use super::*; + #[test] + fn test_bounds() { + let mut vals = vec![16, 45, 45, 45, 82]; + + assert_eq!(slice_upper_bound(&vals, &44), 1); + assert_eq!(slice_lower_bound(&vals, &45), 1); + assert_eq!(slice_upper_bound(&vals, &45), 4); + assert_eq!(slice_lower_bound(&vals, &46), 4); + + vals.dedup(); + for (i, q) in vals.iter().enumerate() { + assert_eq!(slice_lower_bound(&vals, q), i); + assert_eq!(slice_upper_bound(&vals, q), i + 1); + } + } + #[test] fn test_coord_compress() { let mut coords = vec![16, 99, 45, 18]; @@ -49,7 +81,7 @@ mod test { let queries = vec![(0, 10), (10, 19), (20, 29)]; let coords = queries.iter().flat_map(|&(i, j)| vec![i, j + 1]).collect(); let index = SparseIndex::new(coords); - + assert_eq!(index.coords, vec![0, 10, 11, 20, 30]); } From 2cab2636dd08c85e8e7dacbbc7633f8b8fc0908b Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sat, 29 Aug 2020 17:10:09 -0700 Subject: [PATCH 47/71] Shell of convex hull algorithm Inspired by today's Hacker Cup problem, Log Drivin' Hirin' --- src/range_query/sqrt_decomp.rs | 62 +++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/range_query/sqrt_decomp.rs b/src/range_query/sqrt_decomp.rs index 5ba2497..ead4a39 100644 --- a/src/range_query/sqrt_decomp.rs +++ b/src/range_query/sqrt_decomp.rs @@ -1,8 +1,7 @@ -//! A generic implementation of Mo's algorithm, aka Query Sqrt Decomposition. -//! It answers q offline queries over intervals in 0..n by shifting the query -//! interval's endpoints by one position at a time. -//! Each endpoint incurs a total cost of at most n * sqrt(q * L_OP * R_OP). - +/// A generic implementation of Mo's algorithm, aka Query Sqrt Decomposition. +/// It answers q offline queries over intervals in 0..n by shifting the query +/// interval's endpoints by one position at a time. +/// Each endpoint incurs a total cost of at most n * sqrt(q * L_OP * R_OP). pub trait MoState { type Q; type A; @@ -102,6 +101,53 @@ impl MoState for DistinctVals { } } +/// Represents a minimum (lower envelope) of a collection of linear functions of a variable, +/// evaluated using the convex hull trick with square root decomposition. +pub struct PiecewiseLinearFn { + sorted_lines: Vec<(i64, i64)>, + recent_lines: Vec<(i64, i64)>, + merge_threshold: usize, +} + +impl PiecewiseLinearFn { + /// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields + /// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, + /// a threshold of 0 yields O(N + Q log N) time complexity. + pub fn with_merge_threshold(merge_threshold: usize) -> Self { + Self { + sorted_lines: vec![], + recent_lines: vec![], + merge_threshold, + } + } + + /// Replaces this function with the minimum of itself and a provided line + pub fn min_with(&mut self, slope: i64, intercept: i64) { + self.recent_lines.push((slope, intercept)); + } + + fn update_envelope(&mut self) { + self.recent_lines.extend(self.sorted_lines.drain(..)); + self.recent_lines.sort_unstable(); + for (slope, intercept) in self.recent_lines.drain(..) { + // TODO: do convex hull trick algorithm + self.sorted_lines.push((slope, intercept)); + } + } + + fn eval_helper(&self, x: i64) -> i64 { + 0 // TODO: pick actual minimum, or infinity if empty + } + + /// Evaluates the function at x + pub fn evaluate(&mut self, x: i64) -> i64 { + if self.recent_lines.len() > self.merge_threshold { + self.update_envelope(); + } + self.eval_helper(x) + } +} + #[cfg(test)] mod test { use super::*; @@ -115,4 +161,10 @@ mod test { assert_eq!(answers, vec![2, 1, 5, 5]); } + + #[test] + fn test_convex_hull_trick() { + let mut func = PiecewiseLinearFn::with_merge_threshold(3); + // TODO: make test + } } From 93f399434ef2899fa10331b943254bb40ab382fe Mon Sep 17 00:00:00 2001 From: Fr0benius Date: Tue, 1 Sep 2020 18:51:53 -0700 Subject: [PATCH 48/71] Convex hull trick (#14) Finished implementation of convex hull trick with sqrt decomposition --- src/range_query/sqrt_decomp.rs | 80 +++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/src/range_query/sqrt_decomp.rs b/src/range_query/sqrt_decomp.rs index ead4a39..e00bdef 100644 --- a/src/range_query/sqrt_decomp.rs +++ b/src/range_query/sqrt_decomp.rs @@ -103,9 +103,11 @@ impl MoState for DistinctVals { /// Represents a minimum (lower envelope) of a collection of linear functions of a variable, /// evaluated using the convex hull trick with square root decomposition. +#[derive(Debug)] pub struct PiecewiseLinearFn { - sorted_lines: Vec<(i64, i64)>, - recent_lines: Vec<(i64, i64)>, + sorted_lines: Vec<(f64, f64)>, + intersections: Vec, + recent_lines: Vec<(f64, f64)>, merge_threshold: usize, } @@ -116,31 +118,67 @@ impl PiecewiseLinearFn { pub fn with_merge_threshold(merge_threshold: usize) -> Self { Self { sorted_lines: vec![], + intersections: vec![], recent_lines: vec![], merge_threshold, } } /// Replaces this function with the minimum of itself and a provided line - pub fn min_with(&mut self, slope: i64, intercept: i64) { + pub fn min_with(&mut self, slope: f64, intercept: f64) { self.recent_lines.push((slope, intercept)); } fn update_envelope(&mut self) { self.recent_lines.extend(self.sorted_lines.drain(..)); - self.recent_lines.sort_unstable(); - for (slope, intercept) in self.recent_lines.drain(..) { - // TODO: do convex hull trick algorithm - self.sorted_lines.push((slope, intercept)); + self.recent_lines + .sort_unstable_by(|x, y| y.partial_cmp(&x).unwrap()); + self.intersections.clear(); + + for (m1, b1) in self.recent_lines.drain(..) { + while let Some(&(m2, b2)) = self.sorted_lines.last() { + // If slopes are equal, the later line will always have lower + // intercept, so we can get rid of the old one. + if (m1 - m2).abs() > 1e-10f64 { + let new_intersection = (b1 - b2) / (m2 - m1); + if &new_intersection > self.intersections.last().unwrap_or(&f64::MIN) { + self.intersections.push(new_intersection); + break; + } + } + self.intersections.pop(); + self.sorted_lines.pop(); + } + self.sorted_lines.push((m1, b1)); } } - fn eval_helper(&self, x: i64) -> i64 { - 0 // TODO: pick actual minimum, or infinity if empty + fn eval_in_envelope(&self, x: f64) -> f64 { + if self.sorted_lines.is_empty() { + return f64::MAX; + } + let idx = match self + .intersections + .binary_search_by(|y| y.partial_cmp(&x).unwrap()) + { + Ok(k) => k, + Err(k) => k, + }; + let (m, b) = self.sorted_lines[idx]; + m * x + b + } + + fn eval_helper(&self, x: f64) -> f64 { + self.recent_lines + .iter() + .map(|&(m, b)| m * x + b) + .min_by(|x, y| x.partial_cmp(y).unwrap()) + .unwrap_or(f64::MAX) + .min(self.eval_in_envelope(x)) } /// Evaluates the function at x - pub fn evaluate(&mut self, x: i64) -> i64 { + pub fn evaluate(&mut self, x: f64) -> f64 { if self.recent_lines.len() > self.merge_threshold { self.update_envelope(); } @@ -164,7 +202,25 @@ mod test { #[test] fn test_convex_hull_trick() { - let mut func = PiecewiseLinearFn::with_merge_threshold(3); - // TODO: make test + 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], + ]; + for threshold in 0..=lines.len() { + let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); + assert_eq!(func.evaluate(0.0), f64::MAX); + for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { + func.min_with(slope as f64, intercept as f64); + let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); + assert_eq!(expected, &ys[..]); + } + } } } From 51416ce11da20cc0f273437715b3a784f035d399 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Wed, 2 Sep 2020 11:46:26 -0700 Subject: [PATCH 49/71] PartialOrd utility functions, README fix --- README.md | 4 ++-- src/range_query/mod.rs | 21 +++++++++++-------- src/range_query/sqrt_decomp.rs | 37 ++++++++++------------------------ 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 0bf9d50..9f33f28 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Elementary graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/range_query): known colloquially as *segtrees*, coordinate compression, and Mo's query square root decomposition -- [Number thery](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test +- [Associative range query](src/range_query): known colloquially as *segtrees*, coordinate compression, convex hull trick, and Mo's query square root decomposition +- [Number theory](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data ergonomically diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 57155c3..64f0e8a 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -6,19 +6,24 @@ pub use dynamic_arq::{ArqView, DynamicArq}; pub use specs::ArqSpec; pub use static_arq::StaticArq; -/// Assuming slice is sorted, returns the minimum i for which slice[i] >= key, -/// or slice.len() if no such i exists -pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { +/// A comparator on partially ordered elements, that panics if they are incomparable +pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { + a.partial_cmp(b).expect("Comparing incomparable elements") +} + +/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// slice[i] >= key, or slice.len() if no such i exists +pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { slice - .binary_search_by(|x| x.cmp(key).then(std::cmp::Ordering::Greater)) + .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Greater)) .unwrap_err() } -/// Assuming slice is sorted, returns the minimum i for which slice[i] > key, -/// or slice.len() if no such i exists -pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { +/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// slice[i] > key, or slice.len() if no such i exists +pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { slice - .binary_search_by(|x| x.cmp(key).then(std::cmp::Ordering::Less)) + .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Less)) .unwrap_err() } diff --git a/src/range_query/sqrt_decomp.rs b/src/range_query/sqrt_decomp.rs index e00bdef..28f8f78 100644 --- a/src/range_query/sqrt_decomp.rs +++ b/src/range_query/sqrt_decomp.rs @@ -114,7 +114,7 @@ pub struct PiecewiseLinearFn { impl PiecewiseLinearFn { /// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields /// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, - /// a threshold of 0 yields O(N + Q log N) time complexity. + /// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. pub fn with_merge_threshold(merge_threshold: usize) -> Self { Self { sorted_lines: vec![], @@ -131,15 +131,14 @@ impl PiecewiseLinearFn { fn update_envelope(&mut self) { self.recent_lines.extend(self.sorted_lines.drain(..)); - self.recent_lines - .sort_unstable_by(|x, y| y.partial_cmp(&x).unwrap()); + self.recent_lines.sort_unstable_by(super::asserting_cmp); self.intersections.clear(); - for (m1, b1) in self.recent_lines.drain(..) { + for (m1, b1) in self.recent_lines.drain(..).rev() { while let Some(&(m2, b2)) = self.sorted_lines.last() { // If slopes are equal, the later line will always have lower // intercept, so we can get rid of the old one. - if (m1 - m2).abs() > 1e-10f64 { + if (m1 - m2).abs() > 1e-9 { let new_intersection = (b1 - b2) / (m2 - m1); if &new_intersection > self.intersections.last().unwrap_or(&f64::MIN) { self.intersections.push(new_intersection); @@ -153,28 +152,14 @@ impl PiecewiseLinearFn { } } - fn eval_in_envelope(&self, x: f64) -> f64 { - if self.sorted_lines.is_empty() { - return f64::MAX; - } - let idx = match self - .intersections - .binary_search_by(|y| y.partial_cmp(&x).unwrap()) - { - Ok(k) => k, - Err(k) => k, - }; - let (m, b) = self.sorted_lines[idx]; - m * x + b - } - fn eval_helper(&self, x: f64) -> f64 { - self.recent_lines - .iter() + let idx = super::slice_lower_bound(&self.intersections, &x); + std::iter::once(self.sorted_lines.get(idx)) + .flatten() + .chain(self.recent_lines.iter()) .map(|&(m, b)| m * x + b) - .min_by(|x, y| x.partial_cmp(y).unwrap()) - .unwrap_or(f64::MAX) - .min(self.eval_in_envelope(x)) + .min_by(super::asserting_cmp) + .unwrap_or(1e18) } /// Evaluates the function at x @@ -215,7 +200,7 @@ mod test { ]; for threshold in 0..=lines.len() { let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); - assert_eq!(func.evaluate(0.0), f64::MAX); + assert_eq!(func.evaluate(0.0), 1e18); for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { func.min_with(slope as f64, intercept as f64); let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); From dafd95beaa8e350d7cd19a4810958a8557cf9db3 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Wed, 2 Sep 2020 12:07:29 -0700 Subject: [PATCH 50/71] Moved some algorithms to a new file misc.rs --- README.md | 3 +- src/lib.rs | 1 + src/misc.rs | 177 +++++++++++++++++++++++++++++++++ src/range_query/mod.rs | 79 --------------- src/range_query/sqrt_decomp.rs | 94 ----------------- 5 files changed, 180 insertions(+), 174 deletions(-) create mode 100644 src/misc.rs diff --git a/README.md b/README.md index 9f33f28..1b1e7fa 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,10 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Elementary graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration - [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/range_query): known colloquially as *segtrees*, coordinate compression, convex hull trick, and Mo's query square root decomposition +- [Associative range query](src/range_query): known colloquially as *segtrees*, as well as Mo's query square root decomposition - [Number theory](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Scanner](src/scanner.rs): utility for reading input data ergonomically - [String processing](src/string_proc.rs): Knuth-Morris-Pratt and Aho-Corasick string matching, suffix array, Manacher's linear-time palindrome search +- [Miscellaneous algorithms](src/misc.rs): slice binary search, coordinate compression, convex hull trick with sqrt decomposition diff --git a/src/lib.rs b/src/lib.rs index 2e50a88..7a157f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! Algorithms Cookbook in Rust. pub mod graph; pub mod math; +pub mod misc; pub mod range_query; pub mod scanner; pub mod string_proc; diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..4332296 --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,177 @@ +//! Miscellaneous algorithms. + +/// A comparator on partially ordered elements, that panics if they are incomparable +pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { + a.partial_cmp(b).expect("Comparing incomparable elements") +} + +/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// slice[i] >= key, or slice.len() if no such i exists +pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { + slice + .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Greater)) + .unwrap_err() +} + +/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// slice[i] > key, or slice.len() if no such i exists +pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { + slice + .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Less)) + .unwrap_err() +} + +/// A simple data structure for coordinate compression +pub struct SparseIndex { + coords: Vec, +} + +impl SparseIndex { + /// Build an index, given the full set of coordinates to compress. + pub fn new(mut coords: Vec) -> Self { + coords.sort_unstable(); + coords.dedup(); + Self { coords } + } + + /// Return Ok(i) if the coordinate q appears at index i + /// Return Err(i) if q appears between indices i-1 and i + pub fn compress(&self, q: i64) -> Result { + self.coords.binary_search(&q) + } +} + +/// Represents a minimum (lower envelope) of a collection of linear functions of a variable, +/// evaluated using the convex hull trick with square root decomposition. +pub struct PiecewiseLinearFn { + sorted_lines: Vec<(f64, f64)>, + intersections: Vec, + recent_lines: Vec<(f64, f64)>, + merge_threshold: usize, +} + +impl PiecewiseLinearFn { + /// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields + /// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, + /// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. + pub fn with_merge_threshold(merge_threshold: usize) -> Self { + Self { + sorted_lines: vec![], + intersections: vec![], + recent_lines: vec![], + merge_threshold, + } + } + + /// Replaces the represented function with the minimum of itself and a provided line + pub fn min_with(&mut self, slope: f64, intercept: f64) { + self.recent_lines.push((slope, intercept)); + } + + fn update_envelope(&mut self) { + self.recent_lines.extend(self.sorted_lines.drain(..)); + self.recent_lines.sort_unstable_by(asserting_cmp); + self.intersections.clear(); + + for (new_m, new_b) in self.recent_lines.drain(..).rev() { + while let Some(&(last_m, last_b)) = self.sorted_lines.last() { + // If slopes are equal, get rid of the old line as its intercept is higher + if (new_m - last_m).abs() > 1e-9 { + let intr = (new_b - last_b) / (last_m - new_m); + if self.intersections.last().map(|&x| x < intr).unwrap_or(true) { + self.intersections.push(intr); + break; + } + } + self.intersections.pop(); + self.sorted_lines.pop(); + } + self.sorted_lines.push((new_m, new_b)); + } + } + + fn eval_helper(&self, x: f64) -> f64 { + let idx = slice_lower_bound(&self.intersections, &x); + std::iter::once(self.sorted_lines.get(idx)) + .flatten() + .chain(self.recent_lines.iter()) + .map(|&(m, b)| m * x + b) + .min_by(asserting_cmp) + .unwrap_or(1e18) + } + + /// Evaluates the function at x + pub fn evaluate(&mut self, x: f64) -> f64 { + if self.recent_lines.len() > self.merge_threshold { + self.update_envelope(); + } + self.eval_helper(x) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_bounds() { + let mut vals = vec![16, 45, 45, 45, 82]; + + assert_eq!(slice_upper_bound(&vals, &44), 1); + assert_eq!(slice_lower_bound(&vals, &45), 1); + assert_eq!(slice_upper_bound(&vals, &45), 4); + assert_eq!(slice_lower_bound(&vals, &46), 4); + + vals.dedup(); + for (i, q) in vals.iter().enumerate() { + assert_eq!(slice_lower_bound(&vals, q), i); + assert_eq!(slice_upper_bound(&vals, q), i + 1); + } + } + + #[test] + fn test_coord_compress() { + let mut coords = vec![16, 99, 45, 18]; + let index = SparseIndex::new(coords.clone()); + + coords.sort_unstable(); + for (i, q) in coords.into_iter().enumerate() { + assert_eq!(index.compress(q - 1), Err(i)); + assert_eq!(index.compress(q), Ok(i)); + assert_eq!(index.compress(q + 1), Err(i + 1)); + } + } + + #[test] + fn test_range_compress() { + let queries = vec![(0, 10), (10, 19), (20, 29)]; + let coords = queries.iter().flat_map(|&(i, j)| vec![i, j + 1]).collect(); + let index = SparseIndex::new(coords); + + assert_eq!(index.coords, vec![0, 10, 11, 20, 30]); + } + + #[test] + fn test_convex_hull_trick() { + 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], + ]; + for threshold in 0..=lines.len() { + let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); + assert_eq!(func.evaluate(0.0), 1e18); + for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { + func.min_with(slope as f64, intercept as f64); + let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); + assert_eq!(expected, &ys[..]); + } + } + } +} diff --git a/src/range_query/mod.rs b/src/range_query/mod.rs index 64f0e8a..332fb59 100644 --- a/src/range_query/mod.rs +++ b/src/range_query/mod.rs @@ -6,90 +6,11 @@ pub use dynamic_arq::{ArqView, DynamicArq}; pub use specs::ArqSpec; pub use static_arq::StaticArq; -/// A comparator on partially ordered elements, that panics if they are incomparable -pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { - a.partial_cmp(b).expect("Comparing incomparable elements") -} - -/// Assuming slice is totally ordered and sorted, returns the minimum i for which -/// slice[i] >= key, or slice.len() if no such i exists -pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { - slice - .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Greater)) - .unwrap_err() -} - -/// Assuming slice is totally ordered and sorted, returns the minimum i for which -/// slice[i] > key, or slice.len() if no such i exists -pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { - slice - .binary_search_by(|x| asserting_cmp(x, key).then(std::cmp::Ordering::Less)) - .unwrap_err() -} - -/// A simple data structure for coordinate compression -pub struct SparseIndex { - coords: Vec, -} - -impl SparseIndex { - /// Build an index, given the full set of coordinates to compress. - pub fn new(mut coords: Vec) -> Self { - coords.sort_unstable(); - coords.dedup(); - Self { coords } - } - - /// Return Ok(i) if the coordinate q appears at index i - /// Return Err(i) if q appears between indices i-1 and i - pub fn compress(&self, q: i64) -> Result { - self.coords.binary_search(&q) - } -} - #[cfg(test)] mod test { use super::specs::*; use super::*; - #[test] - fn test_bounds() { - let mut vals = vec![16, 45, 45, 45, 82]; - - assert_eq!(slice_upper_bound(&vals, &44), 1); - assert_eq!(slice_lower_bound(&vals, &45), 1); - assert_eq!(slice_upper_bound(&vals, &45), 4); - assert_eq!(slice_lower_bound(&vals, &46), 4); - - vals.dedup(); - for (i, q) in vals.iter().enumerate() { - assert_eq!(slice_lower_bound(&vals, q), i); - assert_eq!(slice_upper_bound(&vals, q), i + 1); - } - } - - #[test] - fn test_coord_compress() { - let mut coords = vec![16, 99, 45, 18]; - let index = SparseIndex::new(coords.clone()); - - coords.sort_unstable(); - for (i, q) in coords.into_iter().enumerate() { - assert_eq!(index.compress(q - 1), Err(i)); - assert_eq!(index.compress(q), Ok(i)); - assert_eq!(index.compress(q + 1), Err(i + 1)); - } - } - - #[test] - fn test_range_compress() { - let queries = vec![(0, 10), (10, 19), (20, 29)]; - let coords = queries.iter().flat_map(|&(i, j)| vec![i, j + 1]).collect(); - let index = SparseIndex::new(coords); - - assert_eq!(index.coords, vec![0, 10, 11, 20, 30]); - } - #[test] fn test_rmq() { let mut arq = StaticArq::::new(&[0; 10]); diff --git a/src/range_query/sqrt_decomp.rs b/src/range_query/sqrt_decomp.rs index 28f8f78..9accc7e 100644 --- a/src/range_query/sqrt_decomp.rs +++ b/src/range_query/sqrt_decomp.rs @@ -101,76 +101,6 @@ impl MoState for DistinctVals { } } -/// Represents a minimum (lower envelope) of a collection of linear functions of a variable, -/// evaluated using the convex hull trick with square root decomposition. -#[derive(Debug)] -pub struct PiecewiseLinearFn { - sorted_lines: Vec<(f64, f64)>, - intersections: Vec, - recent_lines: Vec<(f64, f64)>, - merge_threshold: usize, -} - -impl PiecewiseLinearFn { - /// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields - /// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, - /// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. - pub fn with_merge_threshold(merge_threshold: usize) -> Self { - Self { - sorted_lines: vec![], - intersections: vec![], - recent_lines: vec![], - merge_threshold, - } - } - - /// Replaces this function with the minimum of itself and a provided line - pub fn min_with(&mut self, slope: f64, intercept: f64) { - self.recent_lines.push((slope, intercept)); - } - - fn update_envelope(&mut self) { - self.recent_lines.extend(self.sorted_lines.drain(..)); - self.recent_lines.sort_unstable_by(super::asserting_cmp); - self.intersections.clear(); - - for (m1, b1) in self.recent_lines.drain(..).rev() { - while let Some(&(m2, b2)) = self.sorted_lines.last() { - // If slopes are equal, the later line will always have lower - // intercept, so we can get rid of the old one. - if (m1 - m2).abs() > 1e-9 { - let new_intersection = (b1 - b2) / (m2 - m1); - if &new_intersection > self.intersections.last().unwrap_or(&f64::MIN) { - self.intersections.push(new_intersection); - break; - } - } - self.intersections.pop(); - self.sorted_lines.pop(); - } - self.sorted_lines.push((m1, b1)); - } - } - - fn eval_helper(&self, x: f64) -> f64 { - let idx = super::slice_lower_bound(&self.intersections, &x); - std::iter::once(self.sorted_lines.get(idx)) - .flatten() - .chain(self.recent_lines.iter()) - .map(|&(m, b)| m * x + b) - .min_by(super::asserting_cmp) - .unwrap_or(1e18) - } - - /// Evaluates the function at x - pub fn evaluate(&mut self, x: f64) -> f64 { - if self.recent_lines.len() > self.merge_threshold { - self.update_envelope(); - } - self.eval_helper(x) - } -} - #[cfg(test)] mod test { use super::*; @@ -184,28 +114,4 @@ mod test { assert_eq!(answers, vec![2, 1, 5, 5]); } - - #[test] - fn test_convex_hull_trick() { - 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], - ]; - for threshold in 0..=lines.len() { - let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); - assert_eq!(func.evaluate(0.0), 1e18); - for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { - func.min_with(slope as f64, intercept as f64); - let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); - assert_eq!(expected, &ys[..]); - } - } - } } From f9f6eb1ca312e76af8a5bc2cdfd7db972769afec Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sun, 6 Sep 2020 17:05:35 -0700 Subject: [PATCH 51/71] cleanup using iterator magic --- src/misc.rs | 10 ++--- src/string_proc.rs | 103 +++++++++++++++++++++------------------------ 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/src/misc.rs b/src/misc.rs index 4332296..c3b41bd 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -70,7 +70,7 @@ impl PiecewiseLinearFn { fn update_envelope(&mut self) { self.recent_lines.extend(self.sorted_lines.drain(..)); - self.recent_lines.sort_unstable_by(asserting_cmp); + self.recent_lines.sort_unstable_by(asserting_cmp); // TODO: switch to O(n) merge self.intersections.clear(); for (new_m, new_b) in self.recent_lines.drain(..).rev() { @@ -78,7 +78,7 @@ impl PiecewiseLinearFn { // If slopes are equal, get rid of the old line as its intercept is higher if (new_m - last_m).abs() > 1e-9 { let intr = (new_b - last_b) / (last_m - new_m); - if self.intersections.last().map(|&x| x < intr).unwrap_or(true) { + if self.intersections.last() < Some(&intr) { self.intersections.push(intr); break; } @@ -92,9 +92,9 @@ impl PiecewiseLinearFn { fn eval_helper(&self, x: f64) -> f64 { let idx = slice_lower_bound(&self.intersections, &x); - std::iter::once(self.sorted_lines.get(idx)) - .flatten() - .chain(self.recent_lines.iter()) + self.recent_lines + .iter() + .chain(self.sorted_lines.get(idx)) .map(|&(m, b)| m * x + b) .min_by(asserting_cmp) .unwrap_or(1e18) diff --git a/src/string_proc.rs b/src/string_proc.rs index da3348e..9a9a692 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -61,13 +61,12 @@ impl<'a, C: Eq> Matcher<'a, C> { /// /// ``` /// use contest_algorithms::string_proc::Matcher; - /// let utf8_string = "hello"; - /// - /// let match_from_byte_literal = Matcher::new(b"hello"); - /// - /// let match_from_bytes = Matcher::new(utf8_string.as_bytes()); - /// + /// let byte_string: &[u8] = b"hello"; + /// let utf8_string: &str = "hello"; /// let vec_char: Vec = utf8_string.chars().collect(); + /// + /// let match_from_byte_literal = Matcher::new(byte_string); + /// let match_from_utf8 = Matcher::new(utf8_string.as_bytes()); /// let match_from_chars = Matcher::new(&vec_char); /// /// let vec_int = vec![4, -3, 1]; @@ -93,24 +92,24 @@ impl<'a, C: Eq> Matcher<'a, C> { Self { pattern, fail } } - /// KMP algorithm, sets match_lens[i] = length of longest prefix of pattern + /// KMP algorithm, sets @return[i] = length of longest prefix of pattern /// matching a suffix of text[0..=i]. - pub fn kmp_match(&self, text: &[C]) -> Vec { - let mut match_lens = Vec::with_capacity(text.len()); + pub fn kmp_match(&self, text: impl IntoIterator) -> Vec { let mut len = 0; - for ch in text { - if len == self.pattern.len() { - len = self.fail[len - 1]; - } - while len > 0 && self.pattern[len] != *ch { - len = self.fail[len - 1]; - } - if self.pattern[len] == *ch { - len += 1; - } - match_lens.push(len); - } - match_lens + text.into_iter() + .map(|ch| { + if len == self.pattern.len() { + len = self.fail[len - 1]; + } + while len > 0 && self.pattern[len] != ch { + len = self.fail[len - 1]; + } + if self.pattern[len] == ch { + len += 1; + } + len + }) + .collect() } } @@ -141,7 +140,7 @@ impl MultiMatcher { /// Precomputes the automaton that allows linear-time string matching. /// If there are duplicate patterns, all but one copy will be ignored. - pub fn new(patterns: Vec>) -> Self { + pub fn new(patterns: impl IntoIterator>) -> Self { let mut trie = Trie::default(); let pat_nodes: Vec = patterns.into_iter().map(|pat| trie.insert(pat)).collect(); @@ -171,16 +170,16 @@ impl MultiMatcher { } } - /// Aho-Corasick algorithm, sets match_nodes[i] = node corresponding to + /// Aho-Corasick algorithm, sets @return[i] = node corresponding to /// longest prefix of some pattern matching a suffix of text[0..=i]. - pub fn ac_match(&self, text: &[C]) -> Vec { - let mut match_nodes = Vec::with_capacity(text.len()); + pub fn ac_match(&self, text: impl IntoIterator) -> Vec { let mut node = 0; - for ch in text { - node = Self::next(&self.trie, &self.fail, node, &ch); - match_nodes.push(node); - } - match_nodes + text.into_iter() + .map(|ch| { + node = Self::next(&self.trie, &self.fail, node, &ch); + node + }) + .collect() } /// For each non-empty match, returns where in the text it ends, and the index @@ -235,9 +234,9 @@ impl SuffixArray { } /// Suffix array construction in O(n log n) time. - pub fn new(text: &[u8]) -> Self { - let n = text.len(); - let init_rank = text.iter().map(|&ch| ch as usize).collect::>(); + pub fn new(text: impl IntoIterator) -> Self { + let init_rank = text.into_iter().map(|ch| ch as usize).collect::>(); + let n = init_rank.len(); let mut sfx = Self::counting_sort(0..n, &init_rank, 256); let mut rank = vec![init_rank]; // Invariant at the start of every loop iteration: @@ -291,7 +290,7 @@ impl SuffixArray { /// # Panics /// /// Panics if text is empty. -pub fn palindromes(text: &[T]) -> Vec { +pub fn palindromes(text: &[impl Eq]) -> Vec { let mut pal = Vec::with_capacity(2 * text.len() - 1); pal.push(1); while pal.len() < pal.capacity() { @@ -339,27 +338,21 @@ mod test { #[test] fn test_kmp_matching() { - let text = b"banana"; - let pattern = b"ana"; + let pattern = "ana"; + let text = "banana"; - let matches = Matcher::new(pattern).kmp_match(text); + let matches = Matcher::new(pattern.as_bytes()).kmp_match(text.bytes()); assert_eq!(matches, vec![0, 1, 2, 3, 2, 3]); } #[test] fn test_ac_matching() { - let text = b"banana bans, apple benefits."; - let dict = vec![ - "banana".bytes(), - "benefit".bytes(), - "banapple".bytes(), - "ban".bytes(), - "fit".bytes(), - ]; - - let matcher = MultiMatcher::new(dict); - let match_nodes = matcher.ac_match(text); + let dict = vec!["banana", "benefit", "banapple", "ban", "fit"]; + let text = "banana bans, apple benefits."; + + let matcher = MultiMatcher::new(dict.iter().map(|s| s.bytes())); + let match_nodes = matcher.ac_match(text.bytes()); let end_pos_and_id = matcher.get_end_pos_and_pat_id(&match_nodes); assert_eq!( @@ -370,11 +363,11 @@ mod test { #[test] fn test_suffix_array() { - let text1 = b"bobocel"; - let text2 = b"banana"; + let text1 = "bobocel"; + let text2 = "banana"; - let sfx1 = SuffixArray::new(text1); - let sfx2 = SuffixArray::new(text2); + let sfx1 = SuffixArray::new(text1.bytes()); + let sfx2 = SuffixArray::new(text2.bytes()); assert_eq!(sfx1.sfx, vec![0, 2, 4, 5, 6, 1, 3]); assert_eq!(sfx2.sfx, vec![5, 3, 1, 0, 4, 2]); @@ -393,9 +386,9 @@ mod test { #[test] fn test_palindrome() { - let text = b"banana"; + let text = "banana"; - let pal_len = palindromes(text); + let pal_len = palindromes(text.as_bytes()); assert_eq!(pal_len, vec![1, 0, 1, 0, 3, 0, 5, 0, 3, 0, 1]); } From 8b75731468e79623ef91bdc2bbbcf1434cee40c1 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 8 Sep 2020 17:48:41 -0700 Subject: [PATCH 52/71] Use merge_sorted() to optimize PiecewiseLinearFn --- src/misc.rs | 59 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/misc.rs b/src/misc.rs index c3b41bd..5f4f23d 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -21,6 +21,20 @@ pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { .unwrap_err() } +/// Merge two sorted collections into one +pub fn merge_sorted( + i1: impl IntoIterator, + i2: impl IntoIterator, +) -> Vec { + let (mut i1, mut i2) = (i1.into_iter().peekable(), i2.into_iter().peekable()); + let mut merged = Vec::with_capacity(i1.size_hint().0 + i2.size_hint().0); + while let (Some(a), Some(b)) = (i1.peek(), i2.peek()) { + merged.push(if a <= b { i1.next() } else { i2.next() }.unwrap()); + } + merged.extend(i1.chain(i2)); + merged +} + /// A simple data structure for coordinate compression pub struct SparseIndex { coords: Vec, @@ -41,7 +55,7 @@ impl SparseIndex { } } -/// Represents a minimum (lower envelope) of a collection of linear functions of a variable, +/// Represents a maximum (upper envelope) of a collection of linear functions of a variable, /// evaluated using the convex hull trick with square root decomposition. pub struct PiecewiseLinearFn { sorted_lines: Vec<(f64, f64)>, @@ -63,19 +77,19 @@ impl PiecewiseLinearFn { } } - /// Replaces the represented function with the minimum of itself and a provided line - pub fn min_with(&mut self, slope: f64, intercept: f64) { + /// Replaces the represented function with the maximum of itself and a provided line + pub fn max_with(&mut self, slope: f64, intercept: f64) { self.recent_lines.push((slope, intercept)); } fn update_envelope(&mut self) { - self.recent_lines.extend(self.sorted_lines.drain(..)); - self.recent_lines.sort_unstable_by(asserting_cmp); // TODO: switch to O(n) merge + self.recent_lines.sort_unstable_by(asserting_cmp); self.intersections.clear(); - for (new_m, new_b) in self.recent_lines.drain(..).rev() { + for (new_m, new_b) in merge_sorted(self.recent_lines.drain(..), self.sorted_lines.drain(..)) + { while let Some(&(last_m, last_b)) = self.sorted_lines.last() { - // If slopes are equal, get rid of the old line as its intercept is higher + // If slopes are equal, get rid of the old line as its intercept is lower if (new_m - last_m).abs() > 1e-9 { let intr = (new_b - last_b) / (last_m - new_m); if self.intersections.last() < Some(&intr) { @@ -96,8 +110,8 @@ impl PiecewiseLinearFn { .iter() .chain(self.sorted_lines.get(idx)) .map(|&(m, b)| m * x + b) - .min_by(asserting_cmp) - .unwrap_or(1e18) + .max_by(asserting_cmp) + .unwrap_or(-1e18) } /// Evaluates the function at x @@ -129,6 +143,17 @@ mod test { } } + #[test] + fn test_merge_sorted() { + let vals1 = vec![16, 45, 45, 82]; + let vals2 = vec![-20, 40, 45, 50]; + let vals_merged = vec![-20, 16, 40, 45, 45, 45, 50, 82]; + + assert_eq!(merge_sorted(None, Some(42)), vec![42]); + assert_eq!(merge_sorted(vals1.iter().cloned(), None), vals1); + assert_eq!(merge_sorted(vals1, vals2), vals_merged); + } + #[test] fn test_coord_compress() { let mut coords = vec![16, 99, 45, 18]; @@ -153,22 +178,22 @@ mod test { #[test] fn test_convex_hull_trick() { - let lines = [(0, 3), (1, 0), (-1, 8), (2, -1), (-1, 4)]; + 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], + [-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], ]; for threshold in 0..=lines.len() { let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); - assert_eq!(func.evaluate(0.0), 1e18); + assert_eq!(func.evaluate(0.0), -1e18); for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { - func.min_with(slope as f64, intercept as f64); + func.max_with(slope as f64, intercept as f64); let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); assert_eq!(expected, &ys[..]); } From b7b756bde371d19ac5d387060887fdc448e73a96 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 8 Sep 2020 18:50:05 -0700 Subject: [PATCH 53/71] Refactor helper functions of PiecewiseLinearFn to have clearer responsibilities --- src/misc.rs | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/misc.rs b/src/misc.rs index 5f4f23d..472e9c3 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -78,33 +78,29 @@ impl PiecewiseLinearFn { } /// Replaces the represented function with the maximum of itself and a provided line - pub fn max_with(&mut self, slope: f64, intercept: f64) { - self.recent_lines.push((slope, intercept)); + pub fn max_with(&mut self, new_m: f64, new_b: f64) { + self.recent_lines.push((new_m, new_b)); } - fn update_envelope(&mut self) { - self.recent_lines.sort_unstable_by(asserting_cmp); - self.intersections.clear(); - - for (new_m, new_b) in merge_sorted(self.recent_lines.drain(..), self.sorted_lines.drain(..)) - { - while let Some(&(last_m, last_b)) = self.sorted_lines.last() { - // If slopes are equal, get rid of the old line as its intercept is lower - if (new_m - last_m).abs() > 1e-9 { - let intr = (new_b - last_b) / (last_m - new_m); - if self.intersections.last() < Some(&intr) { - self.intersections.push(intr); - break; - } + /// Similar to max_with but requires that (new_m, new_b) be the largest pair so far + fn max_with_sorted(&mut self, new_m: f64, new_b: f64) { + while let Some(&(last_m, last_b)) = self.sorted_lines.last() { + // If slopes are equal, get rid of the old line as its intercept is lower + if (new_m - last_m).abs() > 1e-9 { + let intersect = (new_b - last_b) / (last_m - new_m); + if self.intersections.last() < Some(&intersect) { + self.intersections.push(intersect); + break; } - self.intersections.pop(); - self.sorted_lines.pop(); } - self.sorted_lines.push((new_m, new_b)); + self.intersections.pop(); + self.sorted_lines.pop(); } + self.sorted_lines.push((new_m, new_b)); } - fn eval_helper(&self, x: f64) -> f64 { + /// Evaluates the function at x + fn eval_unoptimized(&self, x: f64) -> f64 { let idx = slice_lower_bound(&self.intersections, &x); self.recent_lines .iter() @@ -114,12 +110,17 @@ impl PiecewiseLinearFn { .unwrap_or(-1e18) } - /// Evaluates the function at x + /// Evaluates the function at x with good amortized runtime pub fn evaluate(&mut self, x: f64) -> f64 { if self.recent_lines.len() > self.merge_threshold { - self.update_envelope(); + self.recent_lines.sort_unstable_by(asserting_cmp); + self.intersections.clear(); + let all_lines = merge_sorted(self.recent_lines.drain(..), self.sorted_lines.drain(..)); + for (new_m, new_b) in all_lines { + self.max_with_sorted(new_m, new_b); + } } - self.eval_helper(x) + self.eval_unoptimized(x) } } From 4c3cddc235a63e69e79df2fc376765a9ee5f0c81 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 8 Sep 2020 22:25:03 -0700 Subject: [PATCH 54/71] misc.rs -> order.rs, with mergesort --- README.md | 9 +++++---- src/graph/mod.rs | 2 +- src/lib.rs | 2 +- src/{misc.rs => order.rs} | 29 ++++++++++++++++++++++++----- 4 files changed, 31 insertions(+), 11 deletions(-) rename src/{misc.rs => order.rs} (91%) diff --git a/README.md b/README.md index 1b1e7fa..5619c82 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,13 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Basic graph representations](src/graph/mod.rs): adjacency lists, disjoint set union - [Elementary graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration -- [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Associative range query](src/range_query): known colloquially as *segtrees*, as well as Mo's query square root decomposition +- [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow - [Number theory](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test -- [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution +- [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic +- [Ordering algorithms](src/order.rs): binary search, mergesort, coordinate compression, amortized convex hull trick +- [Associative range query](src/range_query): static and dynamic ARQ trees (a.k.a. segtrees), Mo's query square root decomposition - [Scanner](src/scanner.rs): utility for reading input data ergonomically - [String processing](src/string_proc.rs): Knuth-Morris-Pratt and Aho-Corasick string matching, suffix array, Manacher's linear-time palindrome search -- [Miscellaneous algorithms](src/misc.rs): slice binary search, coordinate compression, convex hull trick with sqrt decomposition + diff --git a/src/graph/mod.rs b/src/graph/mod.rs index f72a3c3..7cee5aa 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -5,7 +5,7 @@ //! All methods will panic if given an out-of-bounds element index. pub mod connectivity; pub mod flow; -mod util; +pub mod util; /// Represents a union of disjoint sets. Each set's elements are arranged in a /// tree, whose root is the set's representative. diff --git a/src/lib.rs b/src/lib.rs index 7a157f2..a88d31d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! Algorithms Cookbook in Rust. pub mod graph; pub mod math; -pub mod misc; +pub mod order; pub mod range_query; pub mod scanner; pub mod string_proc; diff --git a/src/misc.rs b/src/order.rs similarity index 91% rename from src/misc.rs rename to src/order.rs index 472e9c3..4c68fdb 100644 --- a/src/misc.rs +++ b/src/order.rs @@ -5,7 +5,7 @@ pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { a.partial_cmp(b).expect("Comparing incomparable elements") } -/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// Assuming slice is sorted and totally ordered, returns the minimum i for which /// slice[i] >= key, or slice.len() if no such i exists pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { slice @@ -13,7 +13,7 @@ pub fn slice_lower_bound(slice: &[T], key: &T) -> usize { .unwrap_err() } -/// Assuming slice is totally ordered and sorted, returns the minimum i for which +/// Assuming slice is sorted and totally ordered, returns the minimum i for which /// slice[i] > key, or slice.len() if no such i exists pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { slice @@ -21,7 +21,7 @@ pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { .unwrap_err() } -/// Merge two sorted collections into one +/// Merge two sorted and totally ordered collections into one pub fn merge_sorted( i1: impl IntoIterator, i2: impl IntoIterator, @@ -35,6 +35,16 @@ pub fn merge_sorted( merged } +/// A stable sort +pub fn merge_sort(mut v: Vec) -> Vec { + if v.len() < 2 { + v + } else { + let v2 = v.split_off(v.len() / 2); + merge_sorted(merge_sort(v), merge_sort(v2)) + } +} + /// A simple data structure for coordinate compression pub struct SparseIndex { coords: Vec, @@ -58,9 +68,9 @@ impl SparseIndex { /// Represents a maximum (upper envelope) of a collection of linear functions of a variable, /// evaluated using the convex hull trick with square root decomposition. pub struct PiecewiseLinearFn { + recent_lines: Vec<(f64, f64)>, sorted_lines: Vec<(f64, f64)>, intersections: Vec, - recent_lines: Vec<(f64, f64)>, merge_threshold: usize, } @@ -70,9 +80,9 @@ impl PiecewiseLinearFn { /// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. pub fn with_merge_threshold(merge_threshold: usize) -> Self { Self { + recent_lines: vec![], sorted_lines: vec![], intersections: vec![], - recent_lines: vec![], merge_threshold, } } @@ -155,6 +165,15 @@ mod test { assert_eq!(merge_sorted(vals1, vals2), vals_merged); } + #[test] + fn test_merge_sort() { + let unsorted = vec![8, -5, 1, 4, -3, 4]; + let sorted = vec![-5, -3, 1, 4, 4, 8]; + + assert_eq!(merge_sort(unsorted), sorted); + assert_eq!(merge_sort(sorted.clone()), sorted); + } + #[test] fn test_coord_compress() { let mut coords = vec![16, 99, 45, 18]; From 5ca02946d763f738bfacad4b57970b8d0cdf5c5a Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sat, 12 Sep 2020 11:26:44 -0700 Subject: [PATCH 55/71] PiecewiseLinearConvexFn documentation --- README.md | 2 +- src/order.rs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5619c82..428ce69 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Rather than try to persuade you with words, this repository aims to show by exam - [Number theory](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test - [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution - [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic -- [Ordering algorithms](src/order.rs): binary search, mergesort, coordinate compression, amortized convex hull trick +- [Ordering algorithms](src/order.rs): binary search, mergesort, coordinate compression, online convex hull trick - [Associative range query](src/range_query): static and dynamic ARQ trees (a.k.a. segtrees), Mo's query square root decomposition - [Scanner](src/scanner.rs): utility for reading input data ergonomically - [String processing](src/string_proc.rs): Knuth-Morris-Pratt and Aho-Corasick string matching, suffix array, Manacher's linear-time palindrome search diff --git a/src/order.rs b/src/order.rs index 4c68fdb..729b3aa 100644 --- a/src/order.rs +++ b/src/order.rs @@ -65,19 +65,22 @@ impl SparseIndex { } } -/// Represents a maximum (upper envelope) of a collection of linear functions of a variable, -/// evaluated using the convex hull trick with square root decomposition. -pub struct PiecewiseLinearFn { +/// Represents a maximum (upper envelope) of a collection of linear functions of one +/// variable, evaluated using an online version of the convex hull trick. +/// It combines the offline algorithm with square root decomposition, resulting in an +/// asymptotically suboptimal but simple algorithm with good amortized performnce: +/// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields +/// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, +/// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. +pub struct PiecewiseLinearConvexFn { recent_lines: Vec<(f64, f64)>, sorted_lines: Vec<(f64, f64)>, intersections: Vec, merge_threshold: usize, } -impl PiecewiseLinearFn { - /// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields - /// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, - /// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. +impl PiecewiseLinearConvexFn { + /// Initializes with a given threshold for re-running the convex hull algorithm pub fn with_merge_threshold(merge_threshold: usize) -> Self { Self { recent_lines: vec![], @@ -210,7 +213,7 @@ mod test { [1, -1, -2, -1, 0, 1], ]; for threshold in 0..=lines.len() { - let mut func = PiecewiseLinearFn::with_merge_threshold(threshold); + let mut func = PiecewiseLinearConvexFn::with_merge_threshold(threshold); assert_eq!(func.evaluate(0.0), -1e18); for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { func.max_with(slope as f64, intercept as f64); From bf762c93bb5e0d4ec7ac2e6261294b89ce93f6c8 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Sat, 12 Sep 2020 13:03:39 -0700 Subject: [PATCH 56/71] Remove merge_threshold parameter and add proof --- src/order.rs | 57 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/order.rs b/src/order.rs index 729b3aa..28492f8 100644 --- a/src/order.rs +++ b/src/order.rs @@ -21,12 +21,13 @@ pub fn slice_upper_bound(slice: &[T], key: &T) -> usize { .unwrap_err() } -/// Merge two sorted and totally ordered collections into one +/// Stably merges two sorted and totally ordered collections into one pub fn merge_sorted( i1: impl IntoIterator, i2: impl IntoIterator, ) -> Vec { - let (mut i1, mut i2) = (i1.into_iter().peekable(), i2.into_iter().peekable()); + let mut i1 = i1.into_iter().peekable(); + let mut i2 = i2.into_iter().peekable(); let mut merged = Vec::with_capacity(i1.size_hint().0 + i2.size_hint().0); while let (Some(a), Some(b)) = (i1.peek(), i2.peek()) { merged.push(if a <= b { i1.next() } else { i2.next() }.unwrap()); @@ -51,15 +52,15 @@ pub struct SparseIndex { } impl SparseIndex { - /// Build an index, given the full set of coordinates to compress. + /// Builds an index, given the full set of coordinates to compress. pub fn new(mut coords: Vec) -> Self { coords.sort_unstable(); coords.dedup(); Self { coords } } - /// Return Ok(i) if the coordinate q appears at index i - /// Return Err(i) if q appears between indices i-1 and i + /// Returns Ok(i) if the coordinate q appears at index i + /// Returns Err(i) if q appears between indices i-1 and i pub fn compress(&self, q: i64) -> Result { self.coords.binary_search(&q) } @@ -68,28 +69,26 @@ impl SparseIndex { /// Represents a maximum (upper envelope) of a collection of linear functions of one /// variable, evaluated using an online version of the convex hull trick. /// It combines the offline algorithm with square root decomposition, resulting in an -/// asymptotically suboptimal but simple algorithm with good amortized performnce: -/// For N inserts interleaved with Q queries, a threshold of N/sqrt(Q) yields -/// O(N sqrt Q + Q log N) time complexity. If all queries come after all inserts, -/// any threshold less than N (e.g., 0) yields O(N + Q log N) time complexity. +/// asymptotically suboptimal but simple algorithm with good amortized performance: +/// N inserts interleaved with Q queries yields O(N sqrt Q + Q log N) time complexity +/// in general, or O(N + Q log N) if all queries come after all inserts. +// Proof: the Q log N term comes from calls to slice_lower_bound(). As for the N sqrt Q, +// note that between successive times when the hull is rebuilt, O(N) work is done, +// and the running totals of insertions and queries satisfy del_N (del_Q + 1) > N. +// Now, either del_Q >= sqrt Q, or else del_Q <= 2 sqrt Q - 1 +// => del_N > N / (2 sqrt Q). +// Since del(N sqrt Q) >= max(N del(sqrt Q), del_N sqrt Q) +// >= max(N del_Q / (2 sqrt Q), del_N sqrt Q), +// we conclude that del(N sqrt Q) >= N / 2. +#[derive(Default)] pub struct PiecewiseLinearConvexFn { recent_lines: Vec<(f64, f64)>, sorted_lines: Vec<(f64, f64)>, intersections: Vec, - merge_threshold: usize, + amortized_work: usize, } impl PiecewiseLinearConvexFn { - /// Initializes with a given threshold for re-running the convex hull algorithm - pub fn with_merge_threshold(merge_threshold: usize) -> Self { - Self { - recent_lines: vec![], - sorted_lines: vec![], - intersections: vec![], - merge_threshold, - } - } - /// Replaces the represented function with the maximum of itself and a provided line pub fn max_with(&mut self, new_m: f64, new_b: f64) { self.recent_lines.push((new_m, new_b)); @@ -125,7 +124,9 @@ impl PiecewiseLinearConvexFn { /// Evaluates the function at x with good amortized runtime pub fn evaluate(&mut self, x: f64) -> f64 { - if self.recent_lines.len() > self.merge_threshold { + self.amortized_work += self.recent_lines.len(); + if self.amortized_work > self.sorted_lines.len() { + self.amortized_work = 0; self.recent_lines.sort_unstable_by(asserting_cmp); self.intersections.clear(); let all_lines = merge_sorted(self.recent_lines.drain(..), self.sorted_lines.drain(..)); @@ -212,14 +213,12 @@ mod test { [1, -1, -2, -3, -3, -3], [1, -1, -2, -1, 0, 1], ]; - for threshold in 0..=lines.len() { - let mut func = PiecewiseLinearConvexFn::with_merge_threshold(threshold); - assert_eq!(func.evaluate(0.0), -1e18); - for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { - func.max_with(slope as f64, intercept as f64); - let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); - assert_eq!(expected, &ys[..]); - } + let mut func = PiecewiseLinearConvexFn::default(); + assert_eq!(func.evaluate(0.0), -1e18); + for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { + func.max_with(slope as f64, intercept as f64); + let ys: Vec = xs.iter().map(|&x| func.evaluate(x as f64) as i64).collect(); + assert_eq!(expected, &ys[..]); } } } From 1c6f43a048a10011362d9a5ef057bdf3a82daedd Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 14 Sep 2020 15:46:52 -0700 Subject: [PATCH 57/71] v0.2.1, revamped README --- .travis.yml | 1 + Cargo.toml | 2 +- README.md | 96 ++++++++++++++++++++++++++++++++++++++++++++-------- src/order.rs | 11 +++++- 4 files changed, 93 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 348c6ba..f4e5b20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,5 @@ 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 67f80ab..c578998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.2.0" +version = "0.2.1" authors = ["Aram Ebtekar"] edition = "2018" diff --git a/README.md b/README.md index 428ce69..a5bcd18 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,15 @@ 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) +- [AtCoder](https://atcoder.jp) - [Kattis](https://open.kattis.com/help/rust) - [SPOJ](https://www.spoj.com/) - [LeetCode](https://leetcode.com/contest) - [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) -- [AtCoder](https://atcoder.jp) -- [Timus](http://acm.timus.ru/help.aspx?topic=rust) For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). @@ -41,17 +41,83 @@ My other goal is to appeal to developers who feel limited by ancient (yet still Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/) -## Contents - -- [Basic graph representations](src/graph/mod.rs): adjacency lists, disjoint set union -- [Elementary graph algorithms](src/graph/util.rs): minimum spanning tree, Euler path, Dijkstra's algorithm, DFS iteration -- [Connected components](src/graph/connectivity.rs): 2-edge-, 2-vertex- and strongly connected components, bridges, articulation points, topological sort, 2-SAT -- [Network flows](src/graph/flow.rs): Dinic's blocking flow, Hopcroft-Karp bipartite matching, min cost max flow -- [Number theory](src/math/mod.rs): canonical solution to Bezout's identity, Miller's primality test -- [FFT](src/math/fft.rs): fast Fourier transform, number theoretic transform, convolution -- [Arithmetic](src/math/num.rs): rational and complex numbers, linear algebra, safe modular arithmetic -- [Ordering algorithms](src/order.rs): binary search, mergesort, coordinate compression, online convex hull trick -- [Associative range query](src/range_query): static and dynamic ARQ trees (a.k.a. segtrees), Mo's query square root decomposition -- [Scanner](src/scanner.rs): utility for reading input data ergonomically -- [String processing](src/string_proc.rs): Knuth-Morris-Pratt and Aho-Corasick string matching, suffix array, Manacher's linear-time palindrome search +# Contents + +## [Graphs](src/graph/) + +### [Graph representations](src/graph/mod.rs) + +- Integer index-based adjacency list representation +- Disjoint set union + +### [Elementary graph algorithms](src/graph/util.rs) + +- Euler path and tour +- Kruskal's minimum spanning tree +- Dijkstra's single-source shortest paths +- DFS pre-order traversal + +### [Connected components](src/graph/connectivity.rs) + +- Connected components +- Strongly connected components +- Bridges and 2-edge-connected components +- Articulation points and 2-vertex-connected components +- Topological sort +- 2-SAT solver + +### [Network flows](src/graph/flow.rs) + +- Dinic's blocking maximum flow +- Hopcroft-Karp bipartite matching +- Minimum cost maximum flow + +## [Math](src/math/) + +### [Number theory](src/math/mod.rs) + +- Greatest common divisor +- Canonical solution to Bezout's identity +- Miller's primality test + +### [Generic FFT](src/math/fft.rs) + +- Fast Fourier transform +- Number theoretic transform +- Convolution + +### [Arithmetic](src/math/num.rs) + +- Rational numbers +- Complex numbers +- Linear algebra +- Safe modular arithmetic + +## [Ordering and search](src/order.rs) + +- Comparator for `PartialOrd` +- Binary search: drop-in replacements for C++ `lower_bound()`/`upper_bound()` +- Merge and mergesort +- Coordinate compression +- Online convex hull trick (update and query the upper envelope of a set of lines) + +## [Associative range query](src/range_query) + +- Statically allocated binary indexed ARQ tree (a.k.a. generic segtree with lazy propagation) +- Dynamically allocated ARQ tree, optionally sparse and persistent +- Mo's algorithm (a.k.a. query square root decomposition) + +## [Scanner](src/scanner.rs) + +- Utility for reading input data ergonomically +- File and standard I/O examples + +## [String processing](src/string_proc.rs) + +- Generic trie +- Knuth-Morris-Pratt single-pattern string matching +- Aho-Corasick multi-pattern string matching +- Suffix array: O(n log n) construction using counting sort +- Longest common prefix +- Manacher's linear-time palindrome search diff --git a/src/order.rs b/src/order.rs index 28492f8..e424258 100644 --- a/src/order.rs +++ b/src/order.rs @@ -1,6 +1,15 @@ -//! Miscellaneous algorithms. +//! Ordering algorithms. /// A comparator on partially ordered elements, that panics if they are incomparable +/// +/// # Example +/// +/// ``` +/// use contest_algorithms::order::asserting_cmp; +/// let mut vec = vec![4.5, -1.7, 1.2]; +/// vec.sort_unstable_by(asserting_cmp); +/// assert_eq!(vec, vec![-1.7, 1.2, 4.5]); +/// ``` pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { a.partial_cmp(b).expect("Comparing incomparable elements") } From f7e1f7190b3ecc4dc8aea55e2816d86e3fd04d18 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Tue, 15 Sep 2020 08:43:23 -0700 Subject: [PATCH 58/71] DFS API now returns edges, skips root --- README.md | 1 + src/graph/mod.rs | 16 ++++++----- src/graph/util.rs | 67 ++++++++++++++++++++++++----------------------- src/order.rs | 2 +- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index a5bcd18..bebe797 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Rather than try to persuade you with words, this repository aims to show by exam ### [Network flows](src/graph/flow.rs) - Dinic's blocking maximum flow +- Minimum cut - Hopcroft-Karp bipartite matching - Minimum cost maximum flow diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 7cee5aa..41d8e30 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -130,14 +130,18 @@ mod test { #[test] fn test_adj_list() { - let mut graph = Graph::new(4, 4); - graph.add_edge(0, 1); + let mut graph = Graph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(2, 4); + graph.add_edge(4, 1); graph.add_edge(1, 2); - graph.add_edge(1, 3); - graph.add_edge(3, 0); + graph.add_undirected_edge(0, 2); - let adj: Vec<(usize, usize)> = graph.adj_list(1).collect(); + let adj = graph.adj_list(2).collect::>(); - assert_eq!(adj, vec![(2, 3), (1, 2)]); + assert_eq!(adj, vec![(5, 0), (1, 4), (0, 3)]); + for (e, v) in adj { + assert_eq!(v, graph.endp[e]); + } } } diff --git a/src/graph/util.rs b/src/graph/util.rs index 489105a..9868721 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -61,14 +61,16 @@ impl Graph { dist } - pub fn dfs(&self, u: usize) -> DfsIterator { + pub fn dfs(&self, root: usize) -> DfsIterator { + let mut visited = vec![false; self.num_v()]; + visited[root] = true; let adj_iters = (0..self.num_v()) .map(|u| self.adj_list(u)) .collect::>(); DfsIterator { - visited: vec![false; self.num_v()], - stack: vec![u], + visited, + stack: vec![root], adj_iters, } } @@ -81,32 +83,23 @@ pub struct DfsIterator<'a> { } impl<'a> Iterator for DfsIterator<'a> { - type Item = usize; + type Item = (usize, usize); - /// Returns next vertex in the DFS + /// Returns next edge and vertex in the depth-first traversal + // Refs: https://www.geeksforgeeks.org/iterative-depth-first-traversal/ + // https://en.wikipedia.org/wiki/Depth-first_search fn next(&mut self) -> Option { - // Sources: - // https://www.geeksforgeeks.org/iterative-depth-first-traversal/ - // https://en.wikipedia.org/wiki/Depth-first_search - while let Some(&u) = self.stack.last() { - if let Some((_, v)) = self.adj_iters[u].next() { + loop { + let &u = self.stack.last()?; + while let Some((e, v)) = self.adj_iters[u].next() { if !self.visited[v] { + self.visited[v] = true; self.stack.push(v); + return Some((e, v)); } - } else { - self.stack.pop(); - } - - // Stack may contain same vertex twice. So - // we return the popped item only - // if it is not visited. - if !self.visited[u] { - self.visited[u] = true; - return Some(u); } + self.stack.pop(); } - - None } } @@ -153,7 +146,7 @@ mod test { #[test] fn test_dfs() { - let mut graph = Graph::new(4, 8); + let mut graph = Graph::new(4, 6); graph.add_edge(0, 2); graph.add_edge(2, 0); graph.add_edge(1, 2); @@ -161,13 +154,17 @@ mod test { graph.add_edge(3, 3); graph.add_edge(2, 3); - let dfs_search = graph.dfs(2).collect::>(); - assert_eq!(dfs_search, vec![2, 3, 0, 1]); + let dfs_root = 2; + let dfs_traversal = std::iter::once(dfs_root) + .chain(graph.dfs(dfs_root).map(|(_, v)| v)) + .collect::>(); + + assert_eq!(dfs_traversal, vec![2, 3, 0, 1]); } #[test] fn test_dfs2() { - let mut graph = Graph::new(5, 8); + let mut graph = Graph::new(5, 6); graph.add_edge(0, 2); graph.add_edge(2, 1); graph.add_edge(1, 0); @@ -175,9 +172,12 @@ mod test { graph.add_edge(3, 4); graph.add_edge(4, 0); - let dfs_search = graph.dfs(0).collect::>(); - //Note this is not the only valid DFS - assert_eq!(dfs_search, vec![0, 3, 4, 2, 1]); + let dfs_root = 0; + let dfs_traversal = std::iter::once(dfs_root) + .chain(graph.dfs(dfs_root).map(|(_, v)| v)) + .collect::>(); + + assert_eq!(dfs_traversal, vec![0, 3, 4, 2, 1]); } #[test] @@ -190,10 +190,11 @@ mod test { } } - let mut dfs_search = graph.dfs(7); - let mut dfs_check = vec![]; - for _ in 0..num_v { - dfs_check.push(dfs_search.next().unwrap()); + let dfs_root = 7; + let mut dfs_search = graph.dfs(dfs_root); + let mut dfs_check = vec![dfs_root]; + for _ in 1..num_v { + dfs_check.push(dfs_search.next().unwrap().1); assert!(dfs_search.stack.len() <= num_v + 1); } diff --git a/src/order.rs b/src/order.rs index e424258..6ba82d9 100644 --- a/src/order.rs +++ b/src/order.rs @@ -80,7 +80,7 @@ impl SparseIndex { /// It combines the offline algorithm with square root decomposition, resulting in an /// asymptotically suboptimal but simple algorithm with good amortized performance: /// N inserts interleaved with Q queries yields O(N sqrt Q + Q log N) time complexity -/// in general, or O(N + Q log N) if all queries come after all inserts. +/// in general, or O((N + Q) log N) if all queries come after all inserts. // Proof: the Q log N term comes from calls to slice_lower_bound(). As for the N sqrt Q, // note that between successive times when the hull is rebuilt, O(N) work is done, // and the running totals of insertions and queries satisfy del_N (del_Q + 1) > N. From 7b0dbd6bd7e1852aa6d42bfb4c3700ce2cba895e Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Wed, 3 Feb 2021 01:11:49 -0800 Subject: [PATCH 59/71] GitHub Badges --- README.md | 6 +++++- src/range_query/dynamic_arq.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bebe797..a3f154a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # Contest Algorithms in Rust +[![Crates.io Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) +[![Documentation](https://docs.rs/contest-algorithms/badge.svg)](https://docs.rs/contest-algorithms) +[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bevyengine/bevy/blob/master/LICENSE) +[![Crates.io Downloads](https://img.shields.io/crates/d/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) [![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) -[![Latest Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) +[![Gitter](https://badges.gitter.im/rust-algos/community.svg)](https://gitter.im/rust-algos/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index 0cc5b90..f33eaaf 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -68,8 +68,10 @@ impl DynamicArq { /// Builds a tree whose leaves are set to a given non-empty slice. pub fn build_from_slice(&mut self, init_val: &[T::S]) -> ArqView { if init_val.len() == 1 { - let mut root = DynamicArqNode::default(); - root.val = init_val[0].clone(); + let root = DynamicArqNode { + val: init_val[0].clone(), + ..Default::default() + }; self.nodes.push(root); (self.nodes.len() - 1, 1) } else { @@ -85,8 +87,10 @@ impl DynamicArq { pub fn merge_equal_sized(&mut self, (lp, ls): ArqView, (rp, rs): ArqView) -> ArqView { assert!(ls == rs || ls + 1 == rs); let p = self.nodes.len(); - let mut root = DynamicArqNode::default(); - root.down = (lp, rp); + let root = DynamicArqNode { + down: (lp, rp), + ..Default::default() + }; self.nodes.push(root); self.pull(p); (p, ls + rs) From 10e42cba71d21b43228dc7a85e75d81e8275d3b1 Mon Sep 17 00:00:00 2001 From: bcpeinhardt <61021968+bcpeinhardt@users.noreply.github.com> Date: Mon, 8 Mar 2021 16:56:47 -0600 Subject: [PATCH 60/71] Proposed Casher struct is in directory proposed_edition (#18) Cacher contribution: memoizes the result of function calls --- src/caching.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 134 insertions(+) create mode 100644 src/caching.rs diff --git a/src/caching.rs b/src/caching.rs new file mode 100644 index 0000000..f3910e7 --- /dev/null +++ b/src/caching.rs @@ -0,0 +1,133 @@ +//! Basic Cacher struct which stores a closure and a hashmap. +//! The hasmap stores key value pairs representing previous +//! function calls. +//! +//! When the Cacher function is run, it first does a lookup +//! to see if the value has already been calculated. If it has, +//! it returns that value. If it hasn't, it calculates the value, +//! adds it to the hashmap, and returns it. + +use std::collections::HashMap; + +/// The Cacher struct (Memoization) stores a function and a Hashmap. +/// The HashMap keeps track of previous input and output for the function so +/// that it only ever has to be called once per input. Use for expensive functions. +pub struct Cacher +where + F: Fn(U) -> V, + U: std::cmp::Eq + std::hash::Hash + Copy, + V: Copy, +{ + calculation: F, + values: HashMap, +} + +impl Cacher +where + F: Fn(U) -> V, + U: std::cmp::Eq + std::hash::Hash + Copy, + V: Copy, +{ + /// Constuctor for the Casher + /// # Examples + /// ``` + /// # use contest_algorithms::caching::Cacher; + /// let mut squared = Cacher::new(|n: u32| n*n); + /// ``` + pub fn new(calculation: F) -> Cacher { + Cacher { + calculation, + values: HashMap::new(), + } + } + + /// Performs a lookup into the HashMap to see if the value has already + /// been calculated. If it has, returns the value. If it has not, + /// calls the function, stores the value, then returns the value + /// # Examples + /// ``` + /// # use contest_algorithms::caching::Cacher; + /// let mut squared = Cacher::new(|n: u32| n*n); + /// + /// // This is where we call the function + /// let sixteen = squared.call(4); + /// ``` + pub fn call(&mut self, arg: U) -> V { + // This is basically the magic of the whole + // structure. You can do this with the entry + // api, but I like how readable this particular + // block of code is. + if let Some(&val) = self.values.get(&arg) { + val + } else { + let val = (self.calculation)(arg); + self.values.insert(arg, val); + val + } + } + + /// Calls the function without performing a lookup and replaces + /// the old calculation with the new one, then returns the value + /// + /// # Use Case + /// If you're wondering, this is for if some sort of "state" has changed + /// underneath you, so your same function call with the same input + /// might now have different output. For instance, if part of your function + /// reads from a file and + /// you think the contents of that file have changed even though the name + /// has not. + pub fn call_and_replace(&mut self, arg: U) -> V { + let new_val = (self.calculation)(arg); + self.values.insert(arg, new_val); + new_val + } +} + +#[cfg(test)] +mod tests { + + use super::Cacher; + use std::collections::HashMap; + + #[test] + fn test_cacher_basically_works() { + let mut word_len = Cacher::new(|word: &str| word.len()); + let hello = word_len.call("hello"); + + // Test function returns correctly + assert_eq!(hello, 5); + + // Test HashMap is correct length + assert_eq!(word_len.values.len(), 1); + + // Test HashMap has correct value after one insert + let mut test_map = HashMap::new(); + test_map.insert("hello", 5); + assert_eq!(word_len.values, test_map); + + // Test HashMap has correct value after duplicate insert + word_len.call("hello"); + assert_eq!(word_len.values, test_map); + + // Test HashMap has correct values after unique input + word_len.call("wazzup"); + test_map.insert("wazzup", 6); + assert_eq!(word_len.values, test_map); + } + + #[test] + fn call_and_replace() { + use std::time::Instant; + + let mut func = Cacher::new(|_param: usize| Instant::now()); + let first_instant = func.call(0); + let lookup_instant = func.call(0); + + assert_eq!(first_instant, lookup_instant); + assert_eq!(1, func.values.len()); + + let second_instant = func.call_and_replace(0); + assert_eq!(1, func.values.len()); + assert_ne!(second_instant, lookup_instant); + } +} diff --git a/src/lib.rs b/src/lib.rs index a88d31d..8e9f1ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ //! Algorithms Cookbook in Rust. +pub mod caching; pub mod graph; pub mod math; pub mod order; From a2d9598f20b69596f9537004705d2b52d0a34d95 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Mon, 8 Mar 2021 15:36:19 -0800 Subject: [PATCH 61/71] Cacher with Entry API --- .travis.yml | 2 +- src/caching.rs | 51 +++++++++++++++++++++++++------------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4e5b20..8340c32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.42.0 # Version currently supported by Codeforces + #- 1.51.0 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/src/caching.rs b/src/caching.rs index f3910e7..668ef75 100644 --- a/src/caching.rs +++ b/src/caching.rs @@ -43,7 +43,7 @@ where /// Performs a lookup into the HashMap to see if the value has already /// been calculated. If it has, returns the value. If it has not, - /// calls the function, stores the value, then returns the value + /// calls the function, stores the value, then returns the value. /// # Examples /// ``` /// # use contest_algorithms::caching::Cacher; @@ -52,30 +52,19 @@ where /// // This is where we call the function /// let sixteen = squared.call(4); /// ``` + // TODO: whenever Rust's Entry API gains the ability to take ownership of + // arg only when necessary, this method should follow the same practice. + // Also, Cacher should implement Fn(U)->V once this is possible. pub fn call(&mut self, arg: U) -> V { - // This is basically the magic of the whole - // structure. You can do this with the entry - // api, but I like how readable this particular - // block of code is. - if let Some(&val) = self.values.get(&arg) { - val - } else { - let val = (self.calculation)(arg); - self.values.insert(arg, val); - val - } + let calc = &self.calculation; + *self.values.entry(arg).or_insert_with_key(|&key| calc(key)) } /// Calls the function without performing a lookup and replaces - /// the old calculation with the new one, then returns the value - /// - /// # Use Case - /// If you're wondering, this is for if some sort of "state" has changed - /// underneath you, so your same function call with the same input - /// might now have different output. For instance, if part of your function - /// reads from a file and - /// you think the contents of that file have changed even though the name - /// has not. + /// the old return value with the new one, and returns it. + /// Potentially useful if the function reads from a file or RNG + /// whose state may have changed. + // TODO: if there's state, FnMut seems more appropriate. pub fn call_and_replace(&mut self, arg: U) -> V { let new_val = (self.calculation)(arg); self.values.insert(arg, new_val); @@ -85,9 +74,7 @@ where #[cfg(test)] mod tests { - - use super::Cacher; - use std::collections::HashMap; + use super::*; #[test] fn test_cacher_basically_works() { @@ -116,7 +103,21 @@ mod tests { } #[test] - fn call_and_replace() { + fn test_cacher_speed() { + // Simulate a function that takes 1 second to complete + let mut func = Cacher::new(|x| { + std::thread::sleep(std::time::Duration::from_millis(100)); + x * x + }); + + // Would take 10 minutes without caching + for _ in 0..6000 { + assert_eq!(25, func.call(5)); + } + } + + #[test] + fn test_call_and_replace() { use std::time::Instant; let mut func = Cacher::new(|_param: usize| Instant::now()); From 1d345542b51e38d9ebe39bf52c855df8ec2929f8 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Thu, 25 Mar 2021 20:31:13 -0700 Subject: [PATCH 62/71] v0.3 with const generics --- .travis.yml | 2 +- Cargo.toml | 2 +- README.md | 2 +- src/math/fft.rs | 13 +++++----- src/math/num.rs | 69 +++++++++++++++++++++++++------------------------ 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8340c32..4b620ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - #- 1.51.0 # Version currently supported by Codeforces + - 1.51.0 # Version currently supported by Codeforces - stable - beta - nightly diff --git a/Cargo.toml b/Cargo.toml index c578998..1b965ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contest-algorithms" -version = "0.2.1" +version = "0.3.0" authors = ["Aram Ebtekar"] edition = "2018" diff --git a/README.md b/README.md index a3f154a..cae59f9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ For help in getting started, you may check out [some of my past submissions](htt My other goal is to appeal to developers who feel limited by ancient (yet still mainstream) programming languages, by demonstrating the power of modern techniques. -Rather than try to persuade you with words, this repository aims to show by example. If you're new to Rust, see [Jim Blandy's *Why Rust?*](http://www.oreilly.com/programming/free/files/why-rust.pdf) for a brief introduction, or just [dive in!](https://doc.rust-lang.org/book/) +Rather than try to persuade you with words, this repository aims to show by example. If you'd like to learn the language, I recommend [the official book](https://doc.rust-lang.org/book/) or [Programming Rust](https://www.amazon.com/Programming-Rust-Fast-Systems-Development-dp-1492052590/dp/1492052590). # Contents diff --git a/src/math/fft.rs b/src/math/fft.rs index f5cfb98..43d83dc 100644 --- a/src/math/fft.rs +++ b/src/math/fft.rs @@ -1,5 +1,5 @@ //! The Fast Fourier Transform (FFT) and Number Theoretic Transform (NTT) -use super::num::{Complex, Field, PI}; +use super::num::{CommonField, Complex, PI}; use std::ops::{Add, Div, Mul, Neg, Sub}; // We can delete this struct once f64::reverse_bits() stabilizes. @@ -30,6 +30,7 @@ impl Iterator for BitRevIterator { } } +#[allow(clippy::upper_case_acronyms)] pub trait FFT: Sized + Copy { type F: Sized + Copy @@ -77,7 +78,7 @@ impl FFT for f64 { // 440564289 and 1713844692 are inverses and 2^27th roots of 1 mod (15<<27)+1 // 125 and 2267742733 are inverses and 2^30th roots of 1 mod (3<<30)+1 impl FFT for i64 { - type F = Field; + type F = CommonField; const ZERO: Self = 0; @@ -197,9 +198,9 @@ mod test { let dft_v = dft_from_reals(&v, v.len()); let new_v: Vec = idft_to_reals(&dft_v, v.len()); - let seven = Field::from(7); - let one = Field::from(1); - let prim = Field::from(15_311_432).pow(1 << 21); + let seven = CommonField::from(7); + let one = CommonField::from(1); + let prim = CommonField::from(15_311_432).pow(1 << 21); let prim2 = prim * prim; let eval0 = seven + one + one; @@ -230,6 +231,6 @@ mod test { let m = convolution(&vec![999], &vec![1_000_000]); assert_eq!(z, vec![14, 30, 6, 4]); - assert_eq!(m, vec![999_000_000 - Field::MOD]); + assert_eq!(m, vec![999_000_000 - super::super::num::COMMON_PRIME]); } } diff --git a/src/math/num.rs b/src/math/num.rs index 7f8df21..ef37619 100644 --- a/src/math/num.rs +++ b/src/math/num.rs @@ -165,27 +165,24 @@ impl Div for Complex { } } -/// Represents an element of the finite (Galois) field of prime order, given by -/// MOD. Until Rust gets const generics, MOD must be hardcoded, but any prime in -/// [1, 2^31.5] will work. If MOD is not prime, ring operations are still valid +/// Represents an element of the finite (Galois) field of prime order M, where +/// 1 <= M < 2^31.5. If M is not prime, ring operations are still valid /// but recip() and division are not. Note that the latter operations are also /// the slowest, so precompute any inverses that you intend to use frequently. #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] -pub struct Field { +pub struct Modulo { pub val: i64, } -impl Field { - pub const MOD: i64 = 998_244_353; // 2^23 * 7 * 17 + 1 - - /// Computes self^exp in O(log(exp)) time - pub fn pow(mut self, mut exp: u64) -> Self { +impl Modulo { + /// Computes self^n in O(log n) time + pub fn pow(mut self, mut n: u64) -> Self { let mut result = Self::from_small(1); - while exp > 0 { - if exp % 2 == 1 { + while n > 0 { + if n % 2 == 1 { result = result * self; } self = self * self; - exp /= 2; + n /= 2; } result } @@ -193,59 +190,63 @@ impl Field { pub fn vec_of_recips(n: i64) -> Vec { let mut recips = vec![Self::from(0), Self::from(1)]; for i in 2..=n { - let (md, dv) = (Self::MOD % i, Self::MOD / i); + let (md, dv) = (M % i, M / i); recips.push(recips[md as usize] * Self::from_small(-dv)); } recips } - /// Computes self^-1 in O(log(Self::MOD)) time + /// Computes self^-1 in O(log M) time pub fn recip(self) -> Self { - self.pow(Self::MOD as u64 - 2) + self.pow(M as u64 - 2) } - /// Avoids the % operation but requires -Self::MOD <= x < Self::MOD + /// Avoids the % operation but requires -M <= x < M fn from_small(s: i64) -> Self { - let val = if s < 0 { s + Self::MOD } else { s }; + let val = if s < 0 { s + M } else { s }; Self { val } } } -impl From for Field { +impl From for Modulo { fn from(val: i64) -> Self { - // Self { val: val.rem_euclid(Self::MOD) } - Self::from_small(val % Self::MOD) + // Self { val: val.rem_euclid(M) } + Self::from_small(val % M) } } -impl Neg for Field { +impl Neg for Modulo { type Output = Self; fn neg(self) -> Self { Self::from_small(-self.val) } } -impl Add for Field { +impl Add for Modulo { type Output = Self; fn add(self, other: Self) -> Self { - Self::from_small(self.val + other.val - Self::MOD) + Self::from_small(self.val + other.val - M) } } -impl Sub for Field { +impl Sub for Modulo { type Output = Self; fn sub(self, other: Self) -> Self { Self::from_small(self.val - other.val) } } -impl Mul for Field { +impl Mul for Modulo { type Output = Self; fn mul(self, other: Self) -> Self { Self::from(self.val * other.val) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Field { +impl Div for Modulo { type Output = Self; fn div(self, other: Self) -> Self { self * other.recip() } } +/// Prime modulus that's commonly used in programming competitions +pub const COMMON_PRIME: i64 = 998_244_353; // 2^23 * 7 * 17 + 1; +pub type CommonField = Modulo; + #[derive(Clone, PartialEq, Debug)] pub struct Matrix { cols: usize, @@ -268,15 +269,15 @@ impl Matrix { let inner = vec.to_vec().into_boxed_slice(); Self { cols, inner } } - pub fn pow(&self, mut exp: u64) -> Self { + pub fn pow(&self, mut n: u64) -> Self { let mut base = self.clone(); let mut result = Self::one(self.cols); - while exp > 0 { - if exp % 2 == 1 { + while n > 0 { + if n % 2 == 1 { result = &result * &base; } base = &base * &base; - exp /= 2; + n /= 2; } result } @@ -417,10 +418,10 @@ mod test { #[test] fn test_field() { - let base = Field::from(1234); + let base = CommonField::from(1234); let zero = base - base; let one = base.recip() * base; - let two = Field::from(2 - 5 * Field::MOD); + let two = CommonField::from(2 - 5 * COMMON_PRIME); assert_eq!(zero.val, 0); assert_eq!(one.val, 1); @@ -430,11 +431,11 @@ mod test { #[test] fn test_vec_of_recips() { - let recips = Field::vec_of_recips(20); + let recips = CommonField::vec_of_recips(20); assert_eq!(recips.len(), 21); for i in 1..recips.len() { - assert_eq!(recips[i], Field::from(i as i64).recip()); + assert_eq!(recips[i], CommonField::from(i as i64).recip()); } } From cf2906b9cc289e6d886f53e19d0e98ec72b9480e Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 30 Jul 2021 12:57:40 -0500 Subject: [PATCH 63/71] Add Z algorithm implementation and test (#19) --- src/string_proc.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/string_proc.rs b/src/string_proc.rs index 9a9a692..f41904c 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -316,6 +316,46 @@ pub fn palindromes(text: &[impl Eq]) -> Vec { pal } +/// Z algorithm for computing an array Z[..], where Z[i] is the length of the +/// longest text substring starting from index i that is **also a prefix** of +/// the text. +/// +/// This runs in O(n) time. It can be embedded in a larger algorithm, or used +/// for string searching as an alternative to KMP above. +/// +/// # Example +/// +/// ``` +/// use contest_algorithms::string_proc::z_algorithm; +/// let z = z_algorithm("ababbababbabababbabababbababbaba".as_bytes()); +/// assert_eq!( +/// z, +/// vec![ +/// 32, 0, 2, 0, 0, 9, 0, 2, 0, 0, 4, 0, 9, 0, 2, 0, 0, 4, 0, 13, 0, 2, +/// 0, 0, 8, 0, 2, 0, 0, 3, 0, 1, +/// ], +/// ); +/// ``` +pub fn z_algorithm(text: &[impl Eq]) -> Vec { + let n = text.len(); + let (mut l, mut r) = (0, 0); + let mut z = vec![0; n]; + z[0] = n; + for i in 1..n { + if i < r { + z[i] = min(r - i, z[i - l]); + } + while i + z[i] < n && text[i + z[i]] == text[z[i]] { + z[i] += 1; + } + if i + z[i] > r { + l = i; + r = i + z[i]; + } + } + z +} + #[cfg(test)] mod test { use super::*; From 64f3755279cd10a63b9852a8dd386d1b0d59b9c2 Mon Sep 17 00:00:00 2001 From: Aram Ebtekar Date: Fri, 30 Jul 2021 11:12:03 -0700 Subject: [PATCH 64/71] Z algorithm without mutating Z[..] --- src/string_proc.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/string_proc.rs b/src/string_proc.rs index f41904c..287ed9a 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -49,8 +49,8 @@ impl Trie { pub struct Matcher<'a, C: Eq> { /// The string pattern to search for. pub pattern: &'a [C], - /// KMP match failure automaton. fail[i] is the length of the longest - /// proper prefix-suffix of pattern[0..=i]. + /// KMP match failure automaton: fail[i] is the length of the longest + /// string that's both a proper prefix and a proper suffix of pattern[0..=i]. pub fail: Vec, } @@ -316,18 +316,18 @@ pub fn palindromes(text: &[impl Eq]) -> Vec { pal } -/// Z algorithm for computing an array Z[..], where Z[i] is the length of the -/// longest text substring starting from index i that is **also a prefix** of -/// the text. +/// Z algorithm: computes the array Z[..], where Z[i] is the length of the +/// longest text prefix of text[i..] that is **also a prefix** of text. /// -/// This runs in O(n) time. It can be embedded in a larger algorithm, or used -/// for string searching as an alternative to KMP above. +/// It runs in O(n) time, maintaining the invariant that l <= i and +/// text[0..r-l] == text[l..r]. It can be embedded in a larger algorithm, +/// or used for string searching as an alternative to KMP. /// /// # Example /// /// ``` /// use contest_algorithms::string_proc::z_algorithm; -/// let z = z_algorithm("ababbababbabababbabababbababbaba".as_bytes()); +/// let z = z_algorithm(b"ababbababbabababbabababbababbaba"); /// assert_eq!( /// z, /// vec![ @@ -338,19 +338,18 @@ pub fn palindromes(text: &[impl Eq]) -> Vec { /// ``` pub fn z_algorithm(text: &[impl Eq]) -> Vec { let n = text.len(); - let (mut l, mut r) = (0, 0); - let mut z = vec![0; n]; - z[0] = n; + let (mut l, mut r) = (1, 1); + let mut z = Vec::with_capacity(n); + z.push(n); for i in 1..n { - if i < r { - z[i] = min(r - i, z[i - l]); - } - while i + z[i] < n && text[i + z[i]] == text[z[i]] { - z[i] += 1; - } - if i + z[i] > r { + if r > i + z[i - l] { + z.push(z[i - l]); + } else { l = i; - r = i + z[i]; + while r < i || (r < n && text[r - i] == text[r]) { + r += 1; + } + z.push(r - i); } } z From dc2fabdbc89c43641e0bd71210a2eecf97bbbcfe Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Sun, 1 Aug 2021 15:40:46 -0500 Subject: [PATCH 65/71] Add Xoshiro256++ random number generator (#21) --- src/graph/connectivity.rs | 2 +- src/lib.rs | 2 + src/rng.rs | 104 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/rng.rs diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 4402e7d..aaf854c 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -45,7 +45,7 @@ impl ConnectivityData { /// /// Multiple-edges and self-loops are correctly handled. pub struct ConnectivityGraph<'a> { - // Immutable graph, frozen for the lifetime of the ConnectivityGraph object. + /// Immutable graph, frozen for the lifetime of the ConnectivityGraph object. pub graph: &'a Graph, /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, diff --git a/src/lib.rs b/src/lib.rs index 8e9f1ac..6a96038 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ //! Algorithms Cookbook in Rust. + pub mod caching; pub mod graph; pub mod math; pub mod order; pub mod range_query; +pub mod rng; pub mod scanner; pub mod string_proc; diff --git a/src/rng.rs b/src/rng.rs new file mode 100644 index 0000000..d25adf0 --- /dev/null +++ b/src/rng.rs @@ -0,0 +1,104 @@ +//! Pseudorandom number generators (PRNGs). + +/// A simple and efficient random number generator. +pub type SmallRng = Xoshiro256PlusPlus; + +/// A xoshiro256++ random number generator. +/// +/// This is a simplified version of the `SmallRng` implementation from the +/// excellent `rand` crate, keeping only essential features. +/// +/// The xoshiro256++ algorithm is not suitable for cryptographic purposes, but +/// is very fast and has excellent statistical properties. +/// +/// * Source: [Docs.rs](https://docs.rs/rand/0.8.4/src/rand/rngs/xoshiro256plusplus.rs.html) +/// * Theory: [Xorshift - Wikipedia](https://en.wikipedia.org/wiki/Xorshift) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Xoshiro256PlusPlus { + s: [u64; 4], +} + +impl Xoshiro256PlusPlus { + /// Construct a new RNG from a 64-bit seed. + pub fn new(mut state: u64) -> Self { + const PHI: u64 = 0x9e3779b97f4a7c15; + let mut seed = <[u64; 4]>::default(); + for chunk in &mut seed { + state = state.wrapping_add(PHI); + let mut z = state; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); + z = z ^ (z >> 31); + *chunk = z; + } + Self { s: seed } + } + + /// Generate a random `u32`. + #[inline] + pub fn next_u32(&mut self) -> u32 { + (self.next_u64() >> 32) as u32 + } + + /// Generate a random `u64`. + #[inline] + pub fn next_u64(&mut self) -> u64 { + let result_plusplus = self.s[0] + .wrapping_add(self.s[3]) + .rotate_left(23) + .wrapping_add(self.s[0]); + + let t = self.s[1] << 17; + + self.s[2] ^= self.s[0]; + self.s[3] ^= self.s[1]; + self.s[1] ^= self.s[2]; + self.s[0] ^= self.s[3]; + + self.s[2] ^= t; + + self.s[3] = self.s[3].rotate_left(45); + + result_plusplus + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_xoshiro256plusplus() { + let mut rng = Xoshiro256PlusPlus::new(42); + assert_eq!(rng.next_u64(), 15021278609987233951); + assert_eq!(rng.next_u64(), 5881210131331364753); + assert_eq!(rng.next_u64(), 18149643915985481100); + assert_eq!(rng.next_u64(), 12933668939759105464); + assert_eq!(rng.next_u64(), 14637574242682825331); + assert_eq!(rng.next_u64(), 10848501901068131965); + assert_eq!(rng.next_u64(), 2312344417745909078); + assert_eq!(rng.next_u64(), 11162538943635311430); + } + + #[test] + fn reference() { + let mut rng = Xoshiro256PlusPlus { s: [1, 2, 3, 4] }; + // These values were produced with the reference implementation: + // http://xoshiro.di.unimi.it/xoshiro256plusplus.c + let expected = [ + 41943041, + 58720359, + 3588806011781223, + 3591011842654386, + 9228616714210784205, + 9973669472204895162, + 14011001112246962877, + 12406186145184390807, + 15849039046786891736, + 10450023813501588000, + ]; + for &e in &expected { + assert_eq!(rng.next_u64(), e); + } + } +} From 7b718430a1cfd96e2a6f10ebd5a59853bff99612 Mon Sep 17 00:00:00 2001 From: Daniel Lawrence Lu Date: Fri, 13 Aug 2021 21:14:48 -0700 Subject: [PATCH 66/71] use i64::MAX instead of 0x3f3f3f3f3f3f3f3f (#22) --- src/graph/flow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 0cd2af6..0b6e684 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -13,7 +13,7 @@ pub struct FlowGraph { impl FlowGraph { /// An upper limit to the flow. - const INF: i64 = 0x3f3f_3f3f_3f3f_3f3f; + const INF: i64 = i64::MAX; /// Initializes an flow network with vmax vertices and no edges. pub fn new(vmax: usize, emax_hint: usize) -> Self { 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 67/71] 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 68/71] 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 69/71] 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 70/71] 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 71/71] 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;