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}