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
//! $ \\sum\_{i=0}\^{n-1} \\left\\lfloor\\frac{ai+b}{m}\\right\\rfloor. $
/// $ \\sum\_{i=0}\^{n-1} \\left\\lfloor\\frac{ai+b}{m}\\right\\rfloor. $
///
/// # Requirements
/// - $n \\ge 0$
/// - $m \\gt 0$
///
/// # Idea
/// あとで書く。
///
/// ## See also
/// - <https://rsk0315.hatenablog.com/entry/2020/12/13/231307>
/// - <https://atcoder.jp/contests/practice2/editorial/579>
///
/// # Examples
/// ```
/// use nekolib::math::LinearFloorSum;
///
/// assert_eq!(4_u128.linear_floor_sum(10, 6, 3), 3);
/// assert_eq!(6_u128.linear_floor_sum(5, 4, 3), 13);
/// assert_eq!(1_u128.linear_floor_sum(1, 0, 0), 0);
/// assert_eq!(31415_u128.linear_floor_sum(92653, 58979, 32384), 314095480);
/// assert_eq!(
/// 1000000000_u128.linear_floor_sum(1000000000, 999999999, 999999999),
/// 499999999500000000
/// );
/// assert_eq!(14_i128.linear_floor_sum(23, -7, -39), -58);
/// ```
pub trait LinearFloorSum {
fn linear_floor_sum(self, m: Self, a: Self, b: Self) -> Self;
}
macro_rules! impl_uint {
($t:ty) => {
impl LinearFloorSum for $t {
fn linear_floor_sum(
self,
mut m: Self,
mut a: Self,
mut b: Self
) -> Self {
let mut n = self;
let mut res = 0;
loop {
if a >= m {
res += n * (n - 1) / 2 * (a / m);
a %= m;
}
if b >= m {
res += n * (b / m);
b %= m;
}
let y = a * n + b;
if y < m {
break;
}
n = y / m;
b = y % m;
std::mem::swap(&mut m, &mut a);
}
res
}
}
};
( $($t:ty)* ) => { $(impl_uint!($t);)* };
}
macro_rules! impl_int {
($t:ty) => {
impl LinearFloorSum for $t {
fn linear_floor_sum(self, m: Self, a: Self, b: Self) -> Self {
let n = self;
let mut res = 0;
assert!(m > 0);
assert!(n >= 0);
let a = if a < 0 {
let a2 = a.rem_euclid(m);
res -= n * (n - 1) / 2 * ((a2 - a) / m);
a2 as u128
} else {
a as u128
};
let b = if b < 0 {
let b2 = b.rem_euclid(m);
res -= n * ((b2 - b) / m);
b2 as u128
} else {
b as u128
};
res + (self as u128).linear_floor_sum(m as u128, a, b) as $t
}
}
};
( $($t:ty)* ) => { $(impl_int!($t);)* };
}
impl_uint!(u8 u16 u32 u64 u128 usize);
impl_int!(i8 i16 i32 i64 i128 isize);