Skip to main content

nekolib/math/
harmonic_floor_sum.rs

1//! $\\sum\_{i=1}^n \\lfloor m/i\\rfloor$ および $\\sum\_{i=1}^n (m\\bmod i)$.
2
3use std::fmt::Debug;
4use std::ops::{
5    Bound::{Excluded, Included, Unbounded},
6    RangeBounds,
7};
8
9/// $\\sum\_{i=1}^n \\lfloor m/i\\rfloor$ および $\\sum\_{i=1}^n (m\\bmod i)$.
10///
11/// # Idea
12/// $\\lfloor m/\\bullet\\rfloor$ の値は $O(\\sqrt{m})$ 通りである。
13/// $i\\in[q\_l, q\_r]$ において $\\lfloor m/i\\rfloor$ の値が共通であるとき、
14/// $\\sum\_{i=q\_l}^{q\_r} \\lfloor m/i\\rfloor$ の値は簡単に求められる。
15/// また、この範囲で $(m\\bmod i)$ は等差数列を成すことから、
16/// $\\sum\_{i=q\_l}^{q\_r} (m\\bmod i)$ も簡単に求められる。
17/// 前計算でこれらの累積和を求めておき、差分計算によってクエリ処理を行う。
18///
19/// # Notes
20/// 考察を進めれば $\\sum\_{i=1}^n \\lfloor\\frac{m}{ai+b}\\rfloor$ を求めることも可能?
21///
22/// # Complexity
23/// $O(\\sqrt{m})$ preprocess, $O(1)$ query time.
24///
25/// # Examples
26/// ```
27/// use nekolib::math::HarmonicFloorSum;
28///
29/// let m = 100;
30/// let hs = HarmonicFloorSum::new(m);
31/// assert_eq!(hs.quot(1..=m), (1..=m).map(|i| m / i).sum());
32/// assert_eq!(hs.rem(1..=m), (1..=m).map(|i| m % i).sum());
33///
34/// let n = 60;
35/// assert_eq!(hs.quot(..=n), (1..=n).map(|i| m / i).sum());
36/// ```
37#[derive(Clone, Debug)]
38pub struct HarmonicFloorSum {
39    m: usize,
40    q: Vec<usize>,
41    qsum: Vec<usize>,
42    rsum: Vec<usize>,
43}
44
45impl HarmonicFloorSum {
46    /// 前処理を行う。
47    ///
48    /// # Examples
49    /// ```
50    /// use nekolib::math::HarmonicFloorSum;
51    ///
52    /// let m = 100;
53    /// let hs = HarmonicFloorSum::new(m);
54    /// ```
55    pub fn new(m: usize) -> Self {
56        let mut q = vec![0];
57        let mut tmp = vec![];
58        for i in (1..).take_while(|&i| i * i <= m) {
59            q.push(i);
60            if i * i < m {
61                tmp.push(m / i);
62            }
63        }
64        q.extend(tmp.into_iter().rev());
65
66        let mut qsum = vec![0; q.len()];
67        let mut rsum = vec![0; q.len()];
68        for i in 1..q.len() {
69            let ql = q[i - 1] + 1;
70            let qr = q[i];
71            let qlen = q[i] - q[i - 1];
72            qsum[i] = qsum[i - 1] + m / q[i] * qlen;
73            rsum[i] = rsum[i - 1] + (m % ql + m % qr) * qlen / 2;
74        }
75        Self { m, q, qsum, rsum }
76    }
77    fn search(&self, n: usize) -> usize {
78        if n > self.m {
79            self.q.len()
80        } else if n * n <= self.m {
81            n
82        } else {
83            self.q.len() - (self.m / n)
84        }
85    }
86    fn quot_internal(&self, n: usize) -> usize {
87        if n == 0 {
88            return 0;
89        }
90        let i = self.search(n);
91        if i == self.q.len() {
92            *self.qsum.last().unwrap()
93        } else if self.q[i] == n {
94            self.qsum[i]
95        } else {
96            self.qsum[i - 1] + (n - self.q[i - 1]) * (self.m / n)
97        }
98    }
99    /// $\\sum\_{i=s}^e \\lfloor m/i\\rfloor$ を返す。
100    ///
101    /// $\\sum\_{i=s}^{\\infty} \\lfloor m/i\\rfloor = \\sum\_{i=s}^m \\lfloor m/i\\rfloor$
102    /// なので、上限を指定しない場合は $m$ までの和を求める。下限を指定しない場合は
103    /// $1$ からの和を求める。
104    ///
105    /// # Examples
106    /// ```
107    /// use nekolib::math::HarmonicFloorSum;
108    ///
109    /// let m = 100;
110    /// let hs = HarmonicFloorSum::new(m);
111    /// assert_eq!(hs.quot(1..=m), (1..=m).map(|i| m / i).sum());
112    /// assert_eq!(hs.quot(..), (1..=m).map(|i| m / i).sum());
113    /// assert_eq!(hs.quot(1..=m), hs.quot(1..=m + 1));
114    /// ```
115    pub fn quot(&self, r: impl RangeBounds<usize>) -> usize {
116        let end = match r.end_bound() {
117            Included(&e) => self.quot_internal(e),
118            Excluded(&e) => self.quot_internal(e - 1),
119            Unbounded => *self.qsum.last().unwrap(),
120        };
121        let start = match r.start_bound() {
122            Included(&s) => self.quot_internal(s - 1),
123            Excluded(&s) => self.quot_internal(s),
124            Unbounded => 0,
125        };
126        end - start
127    }
128    fn rem_internal(&self, n: usize) -> usize {
129        if n == 0 {
130            return 0;
131        }
132        let i = self.search(n);
133        if i == self.q.len() {
134            *self.rsum.last().unwrap() + (n - self.m) * self.m
135        } else if self.q[i] == n {
136            self.rsum[i]
137        } else {
138            let ql = self.q[i - 1] + 1;
139            let len = n - self.q[i - 1];
140            self.rsum[i - 1] + (self.m % n + self.m % ql) * len / 2
141        }
142    }
143    /// $\\sum\_{i=s}^e (m\\bmod i)$ を返す。
144    ///
145    /// 下限を指定しない場合は $1$ からの和を求める。
146    ///
147    /// # Panics
148    /// $\\sum\_{i=s}^{\\infty} (m\\bmod i) = \\infty$ なので、上限が `Unbounded` の場合は
149    /// panic する。
150    ///
151    /// # Examples
152    /// ```
153    /// use nekolib::math::HarmonicFloorSum;
154    ///
155    /// let m = 100;
156    /// let hs = HarmonicFloorSum::new(m);
157    /// assert_eq!(hs.rem(1..=m), (1..=m).map(|i| m % i).sum());
158    /// assert_eq!(hs.rem(..=m), (1..=m).map(|i| m % i).sum());
159    /// assert_ne!(hs.rem(1..=m), hs.rem(1..=m + 1)); // m % (m + 1) = m > 0
160    /// ```
161    ///
162    /// ```should_panic
163    /// use nekolib::math::HarmonicFloorSum;
164    /// let m = 100;
165    /// let hs = HarmonicFloorSum::new(m);
166    /// let infty = hs.rem(1..); // diverges
167    /// ```
168    pub fn rem(&self, r: impl RangeBounds<usize>) -> usize {
169        let end = match r.end_bound() {
170            Included(&e) => self.rem_internal(e),
171            Excluded(&e) => self.rem_internal(e - 1),
172            Unbounded => panic!("the infinite sum does not converge"),
173        };
174        let start = match r.start_bound() {
175            Included(&s) => self.rem_internal(s - 1),
176            Excluded(&s) => self.rem_internal(s),
177            Unbounded => 0,
178        };
179        end - start
180    }
181}
182
183#[test]
184fn test_quot() {
185    let m = 300;
186    let hs = HarmonicFloorSum::new(m);
187    for start in 1..=m + 10 {
188        let mut sum = 0;
189        for end in start..=m + 10 {
190            sum += m / end;
191            assert_eq!(hs.quot(start..=end), sum);
192        }
193        let mut sum = 0;
194        for end in start..=m + 10 {
195            assert_eq!(hs.quot(start..end), sum);
196            sum += m / end;
197        }
198    }
199}
200
201#[test]
202fn test_rem() {
203    let m = 300;
204    let hs = HarmonicFloorSum::new(m);
205    for start in 1..=m + 10 {
206        let mut sum = 0;
207        for end in start..=m + 10 {
208            sum += m % end;
209            assert_eq!(hs.rem(start..=end), sum);
210        }
211        let mut sum = 0;
212        for end in start..=m + 10 {
213            assert_eq!(hs.rem(start..end), sum);
214            sum += m % end;
215        }
216    }
217}