1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! $\\sum\_{i=1}^n \\lfloor m/i\\rfloor$ および $\\sum\_{i=1}^n (m\\bmod i)$.

use std::fmt::Debug;
use std::ops::{
    Bound::{Excluded, Included, Unbounded},
    RangeBounds,
};

/// $\\sum\_{i=1}^n \\lfloor m/i\\rfloor$ および $\\sum\_{i=1}^n (m\\bmod i)$.
///
/// # Idea
/// $\\lfloor m/\\bullet\\rfloor$ の値は $O(\\sqrt{m})$ 通りである。
/// $i\\in[q\_l, q\_r]$ において $\\lfloor m/i\\rfloor$ の値が共通であるとき、
/// $\\sum\_{i=q\_l}^{q\_r} \\lfloor m/i\\rfloor$ の値は簡単に求められる。
/// また、この範囲で $(m\\bmod i)$ は等差数列を成すことから、
/// $\\sum\_{i=q\_l}^{q\_r} (m\\bmod i)$ も簡単に求められる。
/// 前計算でこれらの累積和を求めておき、差分計算によってクエリ処理を行う。
///
/// # Notes
/// 考察を進めれば $\\sum\_{i=1}^n \\lfloor\\frac{m}{ai+b}\\rfloor$ を求めることも可能?
///
/// # Complexity
/// $O(\\sqrt{m})$ preprocess, $O(1)$ query time.
///
/// # Examples
/// ```
/// use nekolib::math::HarmonicFloorSum;
///
/// let m = 100;
/// let hs = HarmonicFloorSum::new(m);
/// assert_eq!(hs.quot(1..=m), (1..=m).map(|i| m / i).sum());
/// assert_eq!(hs.rem(1..=m), (1..=m).map(|i| m % i).sum());
///
/// let n = 60;
/// assert_eq!(hs.quot(..=n), (1..=n).map(|i| m / i).sum());
/// ```
#[derive(Clone, Debug)]
pub struct HarmonicFloorSum {
    m: usize,
    q: Vec<usize>,
    qsum: Vec<usize>,
    rsum: Vec<usize>,
}

impl HarmonicFloorSum {
    /// 前処理を行う。
    ///
    /// # Examples
    /// ```
    /// use nekolib::math::HarmonicFloorSum;
    ///
    /// let m = 100;
    /// let hs = HarmonicFloorSum::new(m);
    /// ```
    pub fn new(m: usize) -> Self {
        let mut q = vec![0];
        let mut tmp = vec![];
        for i in (1..).take_while(|&i| i * i <= m) {
            q.push(i);
            if i * i < m {
                tmp.push(m / i);
            }
        }
        q.extend(tmp.into_iter().rev());

        let mut qsum = vec![0; q.len()];
        let mut rsum = vec![0; q.len()];
        for i in 1..q.len() {
            let ql = q[i - 1] + 1;
            let qr = q[i];
            let qlen = q[i] - q[i - 1];
            qsum[i] = qsum[i - 1] + m / q[i] * qlen;
            rsum[i] = rsum[i - 1] + (m % ql + m % qr) * qlen / 2;
        }
        Self { m, q, qsum, rsum }
    }
    fn search(&self, n: usize) -> usize {
        if n > self.m {
            self.q.len()
        } else if n * n <= self.m {
            n
        } else {
            self.q.len() - (self.m / n)
        }
    }
    fn quot_internal(&self, n: usize) -> usize {
        if n == 0 {
            return 0;
        }
        let i = self.search(n);
        if i == self.q.len() {
            *self.qsum.last().unwrap()
        } else if self.q[i] == n {
            self.qsum[i]
        } else {
            self.qsum[i - 1] + (n - self.q[i - 1]) * (self.m / n)
        }
    }
    /// $\\sum\_{i=s}^e \\lfloor m/i\\rfloor$ を返す。
    ///
    /// $\\sum\_{i=s}^{\\infty} \\lfloor m/i\\rfloor = \\sum\_{i=s}^m \\lfloor m/i\\rfloor$
    /// なので、上限を指定しない場合は $m$ までの和を求める。下限を指定しない場合は
    /// $1$ からの和を求める。
    ///
    /// # Examples
    /// ```
    /// use nekolib::math::HarmonicFloorSum;
    ///
    /// let m = 100;
    /// let hs = HarmonicFloorSum::new(m);
    /// assert_eq!(hs.quot(1..=m), (1..=m).map(|i| m / i).sum());
    /// assert_eq!(hs.quot(..), (1..=m).map(|i| m / i).sum());
    /// assert_eq!(hs.quot(1..=m), hs.quot(1..=m + 1));
    /// ```
    pub fn quot(&self, r: impl RangeBounds<usize>) -> usize {
        let end = match r.end_bound() {
            Included(&e) => self.quot_internal(e),
            Excluded(&e) => self.quot_internal(e - 1),
            Unbounded => *self.qsum.last().unwrap(),
        };
        let start = match r.start_bound() {
            Included(&s) => self.quot_internal(s - 1),
            Excluded(&s) => self.quot_internal(s),
            Unbounded => 0,
        };
        end - start
    }
    fn rem_internal(&self, n: usize) -> usize {
        if n == 0 {
            return 0;
        }
        let i = self.search(n);
        if i == self.q.len() {
            *self.rsum.last().unwrap() + (n - self.m) * self.m
        } else if self.q[i] == n {
            self.rsum[i]
        } else {
            let ql = self.q[i - 1] + 1;
            let len = n - self.q[i - 1];
            self.rsum[i - 1] + (self.m % n + self.m % ql) * len / 2
        }
    }
    /// $\\sum\_{i=s}^e (m\\bmod i)$ を返す。
    ///
    /// 下限を指定しない場合は $1$ からの和を求める。
    ///
    /// # Panics
    /// $\\sum\_{i=s}^{\\infty} (m\\bmod i) = \\infty$ なので、上限が `Unbounded` の場合は
    /// panic する。
    ///
    /// # Examples
    /// ```
    /// use nekolib::math::HarmonicFloorSum;
    ///
    /// let m = 100;
    /// let hs = HarmonicFloorSum::new(m);
    /// assert_eq!(hs.rem(1..=m), (1..=m).map(|i| m % i).sum());
    /// assert_eq!(hs.rem(..=m), (1..=m).map(|i| m % i).sum());
    /// assert_ne!(hs.rem(1..=m), hs.rem(1..=m + 1)); // m % (m + 1) = m > 0
    /// ```
    ///
    /// ```should_panic
    /// use nekolib::math::HarmonicFloorSum;
    /// let m = 100;
    /// let hs = HarmonicFloorSum::new(m);
    /// let infty = hs.rem(1..); // diverges
    /// ```
    pub fn rem(&self, r: impl RangeBounds<usize>) -> usize {
        let end = match r.end_bound() {
            Included(&e) => self.rem_internal(e),
            Excluded(&e) => self.rem_internal(e - 1),
            Unbounded => panic!("the infinite sum does not converge"),
        };
        let start = match r.start_bound() {
            Included(&s) => self.rem_internal(s - 1),
            Excluded(&s) => self.rem_internal(s),
            Unbounded => 0,
        };
        end - start
    }
}

#[test]
fn test_quot() {
    let m = 300;
    let hs = HarmonicFloorSum::new(m);
    for start in 1..=m + 10 {
        let mut sum = 0;
        for end in start..=m + 10 {
            sum += m / end;
            assert_eq!(hs.quot(start..=end), sum);
        }
        let mut sum = 0;
        for end in start..=m + 10 {
            assert_eq!(hs.quot(start..end), sum);
            sum += m / end;
        }
    }
}

#[test]
fn test_rem() {
    let m = 300;
    let hs = HarmonicFloorSum::new(m);
    for start in 1..=m + 10 {
        let mut sum = 0;
        for end in start..=m + 10 {
            sum += m % end;
            assert_eq!(hs.rem(start..=end), sum);
        }
        let mut sum = 0;
        for end in start..=m + 10 {
            assert_eq!(hs.rem(start..end), sum);
            sum += m % end;
        }
    }
}