Skip to content

Commit

Permalink
Deque with aggregate (#93)
Browse files Browse the repository at this point in the history
* saving

* progress

* now ACs

* finish

* rename

* finish docs

* add source

* nit

---------

Co-authored-by: Luke Videckis <[email protected]>
Co-authored-by: Cameron Custer <[email protected]>
  • Loading branch information
3 people authored Jul 26, 2024
1 parent e3b286d commit 7670e46
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 1 deletion.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ path = "examples/monotonic/count_rects.rs"
name = "cartesian_tree"
path = "examples/monotonic/cartesian_tree.rs"

[[example]]
name = "deq_agg"
path = "examples/data_structures/deq_agg.rs"

[[example]]
name = "fenwick_kth"
path = "examples/data_structures/fenwick_kth.rs"
Expand All @@ -238,4 +242,4 @@ path = "examples/graphs/lca_rmq_next_on_path.rs"

[[example]]
name = "linear_rmq"
path = "examples/data_structures/linear_rmq.rs"
path = "examples/data_structures/linear_rmq.rs"
71 changes: 71 additions & 0 deletions examples/data_structures/deq_agg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// verification-helper: PROBLEM https://judge.yosupo.jp/problem/deque_operate_all_composite

use proconio::input;
use programming_team_code_rust::data_structures::deq_agg::DeqAgg;
use rand::{thread_rng, Rng};
use std::collections::VecDeque;

const MOD: u64 = 998_244_353;

fn main() {
let mut rng = thread_rng();
input! {
q: usize,
}

let mut std_deq = VecDeque::new();
let mut deq =
DeqAgg::new(|&i: &(u64, u64), &j: &(u64, u64)| (i.0 * j.0 % MOD, (j.0 * i.1 + j.1) % MOD));

for _ in 0..q {
input! {
t: u8,
}
match t {
0 => {
input! {
a: u64,
b: u64,
}
deq.push_front((a, b));
std_deq.push_front((a, b));
}
1 => {
input! {
a: u64,
b: u64,
}
deq.push_back((a, b));
std_deq.push_back((a, b));
}
2 => {
deq.pop_front();
std_deq.pop_front();
}
3 => {
deq.pop_back();
std_deq.pop_back();
}
_ => {
input! {
x: u64,
}
if let Some((a, b)) = deq.query() {
println!("{}", (a * x + b) % MOD);
} else {
println!("{}", x);
}
}
}
assert_eq!(deq.len(), std_deq.len());
assert_eq!(deq.is_empty(), std_deq.is_empty());
assert_eq!(deq.front(), std_deq.front());
assert_eq!(deq.back(), std_deq.back());
if !std_deq.is_empty() {
for _ in 0..3 {
let i = rng.gen_range(0..deq.len());
assert_eq!(deq[i], std_deq[i]);
}
}
}
}
201 changes: 201 additions & 0 deletions src/data_structures/deq_agg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//! # Deque with Aggregate
/// - see <https://github.com/suisen-cp/cp-library-cpp/blob/main/library/datastructure/deque_aggregation.hpp>
///
/// simulate a deque with 2 stacks:
/// `le`, `ri` are stacks of { number, sum }
/// accumulate
/// <----------- -------> fold numbers from inside
/// ( le ][ ri )
///
/// # Example
/// ```
/// use programming_team_code_rust::data_structures::deq_agg::DeqAgg;
///
/// let mut deq = DeqAgg::new(|&x, &y| std::cmp::max(x, y));
/// deq.push_front(5);
/// deq.push_front(3);
/// deq.push_back(1);
/// assert_eq!(deq[1], 5);
/// assert!(std::panic::catch_unwind(|| deq[3]).is_err());
/// assert_eq!(deq.query(), Some(5));
/// assert_eq!(deq.front(), Some(&3));
/// assert_eq!(deq.pop_front(), Some(3));
/// assert_eq!(deq.back(), Some(&1));
/// assert_eq!(deq.pop_back(), Some(1));
/// ```
pub struct DeqAgg<T, F> {
le: Vec<(T, T)>,
ri: Vec<(T, T)>,
op: F,
}

impl<T: Clone, F: Fn(&T, &T) -> T> DeqAgg<T, F> {
/// Creates new instance of DeqAgg
///
/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn new(op: F) -> Self {
Self {
le: vec![],
ri: vec![],
op,
}
}

/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn len(&self) -> usize {
self.le.len() + self.ri.len()
}

/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn is_empty(&self) -> bool {
self.le.is_empty() && self.ri.is_empty()
}

/// Gets deq\[0\] op deq\[1\] op ... op deq.back(); or None if empty
///
/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn query(&self) -> Option<T> {
if self.is_empty() {
None
} else if self.le.is_empty() {
Some(self.ri.last().unwrap().1.clone())
} else if self.ri.is_empty() {
Some(self.le.last().unwrap().1.clone())
} else {
Some((self.op)(
&self.le.last().unwrap().1,
&self.ri.last().unwrap().1,
))
}
}

/// Gets deq\[0\]; or None if empty
///
/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn front(&self) -> Option<&T> {
if let Some(last) = self.le.last() {
Some(&last.0)
} else if !self.ri.is_empty() {
Some(&self.ri[0].0)
} else {
None
}
}

/// Gets deq\[deq.len() - 1\]; or None if empty
///
/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn back(&self) -> Option<&T> {
if let Some(last) = self.ri.last() {
Some(&last.0)
} else if !self.le.is_empty() {
Some(&self.le[0].0)
} else {
None
}
}

/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn push_front(&mut self, elem: T) {
self.le.push((
elem.clone(),
if let Some(last) = self.le.last() {
(self.op)(&elem, &last.1)
} else {
elem
},
));
}

/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
pub fn push_back(&mut self, elem: T) {
self.ri.push((
elem.clone(),
if let Some(last) = self.ri.last() {
(self.op)(&last.1, &elem)
} else {
elem
},
));
}

/// Removes front, and returns it; or None if empty
///
/// # Complexity
/// - Time: O(1) ammortized
/// - Space: O(1) ammortized
pub fn pop_front(&mut self) -> Option<T> {
if self.le.is_empty() {
let mut ary = vec![];
std::mem::swap(&mut ary, &mut self.ri);
self.rebuild((ary.len() + 1) / 2, ary);
}
self.le.pop().map(|elem| elem.0)
}

/// Removes back, and returns it; or None if empty
///
/// # Complexity
/// - Time: O(1) ammortized
/// - Space: O(1) ammortized
pub fn pop_back(&mut self) -> Option<T> {
if self.ri.is_empty() {
let mut ary = vec![];
std::mem::swap(&mut ary, &mut self.le);
ary.reverse();
self.rebuild(ary.len() / 2, ary);
}
self.ri.pop().map(|elem| elem.0)
}

fn rebuild(&mut self, le_len: usize, mut ary: Vec<(T, T)>) {
self.ri = ary.split_off(le_len);
self.le = ary;
self.le.reverse();
for i in 0..self.le.len() {
self.le[i].1 = if i == 0 {
self.le[0].0.clone()
} else {
(self.op)(&self.le[i].0, &self.le[i - 1].1)
};
}
for i in 0..self.ri.len() {
self.ri[i].1 = if i == 0 {
self.ri[0].0.clone()
} else {
(self.op)(&self.ri[i - 1].1, &self.ri[i].0)
};
}
}
}

/// # Complexity
/// - Time: O(1)
/// - Space: O(1)
impl<T, F> std::ops::Index<usize> for DeqAgg<T, F> {
type Output = T;
fn index(&self, i: usize) -> &Self::Output {
if i < self.le.len() {
&self.le[self.le.len() - i - 1].0
} else {
&self.ri[i - self.le.len()].0
}
}
}
1 change: 1 addition & 0 deletions src/data_structures/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! # Data Structures
pub mod binary_trie;
pub mod deq_agg;
pub mod disjoint_rmq;
pub mod fenwick;
pub mod lazy_seg_tree;
Expand Down

0 comments on commit 7670e46

Please sign in to comment.