Skip to main content

nekolib/math/
slope_function.rs

1//! 区分線形凸関数。
2
3use std::cmp::Reverse;
4use std::collections::BinaryHeap;
5use std::fmt::Debug;
6use std::ops::{Add, AddAssign, Bound, RangeInclusive};
7
8/// 区分線形凸関数。
9///
10/// 整数の多重集合 $L$, $R$ に対して、次の形で表せる関数を管理する:
11/// $$ f(x) = c + \\sum\_{l\\in L} (l-x)\_+ + \\sum\_{r\\in R} (x-r)\_+. $$
12/// ここで、$(a-x)\_+ = \\max\\{0, a-x\\}$、$(x-a)\_+ = \\max\\{0, x-a\\}$ とする。
13/// たとえば、$|x-a| = (a-x)\_+ + (x-a)\_+$ と書ける。
14///
15/// # Idea
16/// `todo!()`
17///
18/// # Complexity
19/// |演算|時間計算量|
20/// |---|---|
21/// |`new`|$O(1)$|
22/// |`add_const`|$O(1)$|
23/// |`add_left`, `add_right`, `add_abs`|$O(\\log(\|L\|) + \\log(\|R\|))$|
24/// |`min_left`, `min_right`|amortized $O(1)$|
25/// |`shift`, `window`|$O(1)$|
26/// |`min`, `argmin`|$O(1)$|
27///
28/// # Examples
29/// ```
30/// use std::ops::Bound::{Included, Unbounded};
31///
32/// use nekolib::math::SlopeFunction;
33///
34/// let mut sf = SlopeFunction::new();
35/// sf.add_left(-1);
36/// sf.add_left(3);
37/// sf.add_right(2);
38/// sf.add_const(-10);
39/// // f(x) = 0.max(-1-x) + 0.max(3-x) + 0.max(x-2) - 10
40/// //   x  | -5 -4 -3 -2 -1  0  1  2  3  4  5
41/// // f(x) |  2  0 -2 -4 -6 -7 -8 -9 -9 -8 -7
42/// assert_eq!(sf.min(), -9);
43/// assert_eq!(sf.argmin(), (Included(2), Included(3)));
44/// ```
45///
46/// # Notes
47/// $(a\_1, a\_2, \\dots, a\_n)$ の中央値を $a\_{\\text{med}}$ とすると、
48/// $\\sum\_{i=1}^n |x-a\_i|$ は $x = a\_{\\text{med}}$ のとき最小となる。
49/// このことから、値の追加と中央値を求めるクエリを処理できる。
50///
51/// ```
52/// use std::ops::Bound::{Included, Excluded, Unbounded};
53///
54/// use nekolib::math::SlopeFunction;
55///
56/// #[derive(Clone, Default)]
57/// struct IncrementalMedian(SlopeFunction<i32>);
58///
59/// impl IncrementalMedian {
60///     fn new() -> Self { Self::default() }
61///     fn insert(&mut self, a: i32) { self.0.add_abs(a); }
62///     fn median(&self) -> Option<i32> {
63///         match self.0.argmin().0 {
64///             Included(x) => Some(x),
65///             Excluded(_) => unreachable!(),
66///             Unbounded => None,
67///         }
68///     }
69/// }
70///
71/// let mut im = IncrementalMedian::new();
72/// assert_eq!(im.median(), None); // {{}}
73/// im.insert(2);
74/// assert_eq!(im.median(), Some(2)); // {{2}}
75/// im.insert(3);
76/// assert_eq!(im.median(), Some(2)); // {{2, 3}}
77/// im.insert(1);
78/// assert_eq!(im.median(), Some(2)); // {{1, 2, 3}}
79/// im.insert(1);
80/// assert_eq!(im.median(), Some(1)); // {{1, 1, 2, 3}}
81/// ```
82///
83/// # References
84/// - <https://maspypy.com/slope-trick-1-%E8%A7%A3%E8%AA%AC%E7%B7%A8>
85#[derive(Clone, Debug, Default)]
86pub struct SlopeFunction<I: Ord> {
87    left: BinaryHeap<I>,
88    right: BinaryHeap<Reverse<I>>,
89    min: I,
90    shl: I,
91    shr: I,
92}
93
94impl<I: SlopeTrickInt> SlopeFunction<I> {
95    /// $f(x) = 0$ で初期化する。
96    ///
97    /// # Examples
98    /// ```
99    /// use std::ops::Bound::Unbounded;
100    ///
101    /// use nekolib::math::SlopeFunction;
102    ///
103    /// let sf = SlopeFunction::<i32>::new();
104    /// assert_eq!(sf.min(), 0);
105    /// assert_eq!(sf.argmin(), (Unbounded, Unbounded));
106    /// ```
107    pub fn new() -> Self { Self::default() }
108    /// $f(x) \\xleftarrow{+} c$ で更新する。
109    ///
110    /// # Examples
111    /// ```
112    /// use nekolib::math::SlopeFunction;
113    ///
114    /// let mut sf = SlopeFunction::new();
115    /// assert_eq!(sf.min(), 0);
116    /// sf.add_const(3);
117    /// assert_eq!(sf.min(), 3);
118    /// sf.add_const(-1);
119    /// assert_eq!(sf.min(), 2);
120    /// ```
121    pub fn add_const(&mut self, c: I) { self.min += c; }
122    /// $f(x) \\xleftarrow{+} (l-x)\_+$ で更新する。
123    ///
124    /// # Examples
125    /// ```
126    /// use std::ops::Bound::{Included, Unbounded};
127    ///
128    /// use nekolib::math::SlopeFunction;
129    ///
130    /// let mut sf = SlopeFunction::new();
131    /// sf.add_left(4);
132    /// assert_eq!(sf.argmin(), (Included(4), Unbounded));
133    /// ```
134    pub fn add_left(&mut self, l: I) {
135        if self.right.is_empty() {
136            self.left.push(l);
137            return;
138        }
139        self.min += l.doz(self.right.peek().unwrap().0);
140        self.right.push(Reverse(l));
141        let l = self.right.pop().unwrap().0;
142        self.left.push(l);
143    }
144    /// $f(x) \\xleftarrow{+} (x-r)\_+$ で更新する。
145    ///
146    /// # Examples
147    /// ```
148    /// use std::ops::Bound::{Included, Unbounded};
149    ///
150    /// use nekolib::math::SlopeFunction;
151    ///
152    /// let mut sf = SlopeFunction::new();
153    /// sf.add_right(4);
154    /// assert_eq!(sf.argmin(), (Unbounded, Included(4)));
155    /// ```
156    pub fn add_right(&mut self, r: I) {
157        if self.left.is_empty() {
158            self.right.push(Reverse(r));
159            return;
160        }
161        self.min += self.left.peek().unwrap().doz(r);
162        self.left.push(r);
163        let r = self.left.pop().unwrap();
164        self.right.push(Reverse(r));
165    }
166    /// $f(x) \\xleftarrow{+} |x-a|$ で更新する。
167    ///
168    /// # Examples
169    /// ```
170    /// use std::ops::Bound::Included;
171    ///
172    /// use nekolib::math::SlopeFunction;
173    ///
174    /// let mut sf = SlopeFunction::new();
175    /// sf.add_abs(4);
176    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
177    /// ```
178    pub fn add_abs(&mut self, a: I) {
179        self.add_left(a);
180        self.add_right(a);
181    }
182    /// $g(x) = \\min\_{y\\le x} f(y)$ として、$f\\gets g$ で更新する。
183    ///
184    /// # Examples
185    /// ```
186    /// use std::ops::Bound::{Included, Unbounded};
187    ///
188    /// use nekolib::math::SlopeFunction;
189    ///
190    /// let mut sf = SlopeFunction::new();
191    /// sf.add_abs(4);
192    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
193    /// sf.min_left();
194    /// assert_eq!(sf.argmin(), (Included(4), Unbounded));
195    /// ```
196    pub fn min_left(&mut self) { self.right.clear(); }
197    /// $g(x) = \\min\_{y\\ge x} f(y)$ として、$f\\gets g$ で更新する。
198    ///
199    /// # Examples
200    /// ```
201    /// use std::ops::Bound::{Included, Unbounded};
202    ///
203    /// use nekolib::math::SlopeFunction;
204    ///
205    /// let mut sf = SlopeFunction::new();
206    /// sf.add_abs(4);
207    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
208    /// sf.min_right();
209    /// assert_eq!(sf.argmin(), (Unbounded, Included(4)));
210    /// ```
211    pub fn min_right(&mut self) { self.left.clear(); }
212    /// $g(x) = f(x-a)$ として、$f\\gets g$ で更新する。
213    ///
214    /// # Examples
215    /// ```
216    /// use std::ops::Bound::Included;
217    ///
218    /// use nekolib::math::SlopeFunction;
219    ///
220    /// let mut sf = SlopeFunction::new();
221    /// sf.add_abs(4);
222    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
223    /// sf.shift(2);
224    /// assert_eq!(sf.argmin(), (Included(6), Included(6)));
225    /// ```
226    pub fn shift(&mut self, s: I) {
227        self.shl += s;
228        self.shr += s;
229    }
230    /// $[a, b]$ に対して $g(x) = \\min\_{y\\in[x-b, x-a]} f(y)$ として、$f\\gets g$ で更新する。
231    ///
232    /// # Examples
233    /// ```
234    /// use std::ops::Bound::Included;
235    ///
236    /// use nekolib::math::SlopeFunction;
237    ///
238    /// let mut sf = SlopeFunction::new();
239    /// sf.add_abs(4);
240    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
241    /// sf.window(-1..=2);
242    /// assert_eq!(sf.argmin(), (Included(3), Included(6)));
243    /// ```
244    pub fn window(&mut self, window: RangeInclusive<I>) {
245        self.shl += *window.start();
246        self.shr += *window.end();
247    }
248    /// $\\min\_{x\\in\\mathbb{R}} f(x)$ を返す。
249    ///
250    /// # Examples
251    /// ```
252    /// use nekolib::math::SlopeFunction;
253    ///
254    /// let mut sf = SlopeFunction::new();
255    /// sf.add_abs(4);
256    /// sf.add_const(1);
257    /// assert_eq!(sf.min(), 1);
258    /// ```
259    pub fn min(&self) -> I { self.min }
260    /// $\\argmin\_{x\\in\\mathbb{R}} f(x)$ を返す。
261    ///
262    /// # Examples
263    /// ```
264    /// use std::ops::Bound::Included;
265    /// use nekolib::math::SlopeFunction;
266    ///
267    /// let mut sf = SlopeFunction::new();
268    /// sf.add_abs(4);
269    /// sf.add_const(1);
270    /// assert_eq!(sf.argmin(), (Included(4), Included(4)));
271    /// ```
272    pub fn argmin(&self) -> (Bound<I>, Bound<I>) {
273        let left = match self.left.peek() {
274            Some(&x) => Bound::Included(x + self.shl),
275            None => Bound::Unbounded,
276        };
277        let right = match self.right.peek() {
278            Some(&Reverse(x)) => Bound::Included(x + self.shr),
279            None => Bound::Unbounded,
280        };
281        (left, right)
282    }
283}
284
285pub trait SlopeTrickInt:
286    Copy + Add<Output = Self> + AddAssign + Default + Ord
287{
288    // unsigned でいうところの saturating_sub
289    fn doz(self, rhs: Self) -> Self;
290}
291
292macro_rules! impl_slope_trick_int {
293    ( $($ty:tt)* ) => { $(
294        impl SlopeTrickInt for $ty {
295            fn doz(self, rhs: Self) -> Self {
296                0.max(self - rhs)
297            }
298        }
299    )* }
300}
301
302impl_slope_trick_int! { i8 i16 i32 i64 i128 isize }