Skip to main content

nekolib/math/
linear_floor_sum.rs

1//! $ \\sum\_{i=0}\^{n-1} \\left\\lfloor\\frac{ai+b}{m}\\right\\rfloor. $
2
3/// $ \\sum\_{i=0}\^{n-1} \\left\\lfloor\\frac{ai+b}{m}\\right\\rfloor. $
4///
5/// # Requirements
6/// - $n \\ge 0$
7/// - $m \\gt 0$
8///
9/// # Idea
10/// あとで書く。
11///
12/// ## See also
13/// - <https://rsk0315.hatenablog.com/entry/2020/12/13/231307>
14/// - <https://atcoder.jp/contests/practice2/editorial/579>
15///
16/// # Examples
17/// ```
18/// use nekolib::math::LinearFloorSum;
19///
20/// assert_eq!(4_u128.linear_floor_sum(10, 6, 3), 3);
21/// assert_eq!(6_u128.linear_floor_sum(5, 4, 3), 13);
22/// assert_eq!(1_u128.linear_floor_sum(1, 0, 0), 0);
23/// assert_eq!(31415_u128.linear_floor_sum(92653, 58979, 32384), 314095480);
24/// assert_eq!(
25///     1000000000_u128.linear_floor_sum(1000000000, 999999999, 999999999),
26///     499999999500000000
27/// );
28/// assert_eq!(14_i128.linear_floor_sum(23, -7, -39), -58);
29/// ```
30pub trait LinearFloorSum {
31    fn linear_floor_sum(self, m: Self, a: Self, b: Self) -> Self;
32}
33
34macro_rules! impl_uint {
35    ($t:ty) => {
36        impl LinearFloorSum for $t {
37            fn linear_floor_sum(
38                self,
39                mut m: Self,
40                mut a: Self,
41                mut b: Self
42            ) -> Self {
43                let mut n = self;
44                let mut res = 0;
45                loop {
46                    if a >= m {
47                        res += n * (n - 1) / 2 * (a / m);
48                        a %= m;
49                    }
50                    if b >= m {
51                        res += n * (b / m);
52                        b %= m;
53                    }
54                    let y = a * n + b;
55                    if y < m {
56                        break;
57                    }
58                    n = y / m;
59                    b = y % m;
60                    std::mem::swap(&mut m, &mut a);
61                }
62                res
63            }
64        }
65    };
66    ( $($t:ty)* ) => { $(impl_uint!($t);)* };
67}
68
69macro_rules! impl_int {
70    ($t:ty) => {
71        impl LinearFloorSum for $t {
72            fn linear_floor_sum(self, m: Self, a: Self, b: Self) -> Self {
73                let n = self;
74                let mut res = 0;
75                assert!(m > 0);
76                assert!(n >= 0);
77                let a = if a < 0 {
78                    let a2 = a.rem_euclid(m);
79                    res -= n * (n - 1) / 2 * ((a2 - a) / m);
80                    a2 as u128
81                } else {
82                    a as u128
83                };
84                let b = if b < 0 {
85                    let b2 = b.rem_euclid(m);
86                    res -= n * ((b2 - b) / m);
87                    b2 as u128
88                } else {
89                    b as u128
90                };
91                res + (self as u128).linear_floor_sum(m as u128, a, b) as $t
92            }
93        }
94    };
95    ( $($t:ty)* ) => { $(impl_int!($t);)* };
96}
97
98impl_uint!(u8 u16 u32 u64 u128 usize);
99impl_int!(i8 i16 i32 i64 i128 isize);