Skip to content

floor_sum.hpp

SECTIONMath INCLUDEnoya/math/floor_sum.hpp

计算任意整数区间上的整除下取整之和,支持系数为负。

\[ \displaystyle F=\sum_{i=L}^{R-1}\left\lfloor\frac{ai+b}{m}\right\rfloor \]

Complexity: Time: O(log m). Space: O(1).

AC 记录:sum_of_floor_of_linear。

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include。

/// @complexity Time: O(log m).
/// Space: O(1).

#include <cassert>
#include <cstdint>
#include <utility>

namespace noya {

using floor_sum_result = __int128_t;

namespace floor_sum_internal {

inline floor_sum_result floor_div(floor_sum_result val, floor_sum_result mod) {
  floor_sum_result quo = val / mod;
  if (val % mod < 0) {
    quo--;
  }
  return quo;
}

} // namespace floor_sum_internal

/// @brief Sum floor((a*i+b)/mod) over i in [l, r), allowing signed
/// l, a, and b; the exact result must fit in signed 128 bits.
inline floor_sum_result floor_sum_range(std::int64_t l, std::int64_t r,
                                        std::int64_t a, std::int64_t b,
                                        std::int64_t mod) {
  assert(l <= r && mod > 0);
  using floor_sum_internal::floor_div;
  floor_sum_result n = floor_sum_result(r) - l;
  floor_sum_result m = mod;
  floor_sum_result k = a;
  floor_sum_result ntr = floor_sum_result(a) * l + b;
  // Accumulate modulo 2^128: intermediate positive/negative contributions can
  // exceed signed 128 bits even when their exact sum satisfies the contract.
  using u128 = unsigned __int128;
  u128 res = 0;
  auto triangle = [](floor_sum_result cnt) -> u128 {
    u128 x = u128(cnt), y = cnt == 0 ? 0 : u128(cnt - 1);
    return x % 2 == 0 ? x / 2 * y : x * (y / 2);
  };

  floor_sum_result sq = floor_div(k, m);
  k -= sq * m;
  res += triangle(n) * u128(sq);
  floor_sum_result iq = floor_div(ntr, m);
  ntr -= iq * m;
  res += u128(n) * u128(iq);

  while (true) {
    if (k >= m) {
      res += triangle(n) * u128(k / m);
      k %= m;
    }
    if (ntr >= m) {
      res += u128(n) * u128(ntr / m);
      ntr %= m;
    }
    floor_sum_result mx = k * n + ntr;
    if (mx < m) {
      break;
    }
    n = mx / m;
    ntr = mx % m;
    std::swap(m, k);
  }
  return static_cast<floor_sum_result>(res);
}

} // namespace noya

 // NOYA_FLOOR_SUM_HPP
#ifndef NOYA_FLOOR_SUM_HPP
#define NOYA_FLOOR_SUM_HPP 1

/// @complexity Time: O(log m).
/// Space: O(1).

#include <cassert>
#include <cstdint>
#include <utility>

namespace noya {

using floor_sum_result = __int128_t;

namespace floor_sum_internal {

inline floor_sum_result floor_div(floor_sum_result val, floor_sum_result mod) {
  floor_sum_result quo = val / mod;
  if (val % mod < 0) {
    quo--;
  }
  return quo;
}

} // namespace floor_sum_internal

/// @brief Sum floor((a*i+b)/mod) over i in [l, r), allowing signed
/// l, a, and b; the exact result must fit in signed 128 bits.
inline floor_sum_result floor_sum_range(std::int64_t l, std::int64_t r,
                                        std::int64_t a, std::int64_t b,
                                        std::int64_t mod) {
  assert(l <= r && mod > 0);
  using floor_sum_internal::floor_div;
  floor_sum_result n = floor_sum_result(r) - l;
  floor_sum_result m = mod;
  floor_sum_result k = a;
  floor_sum_result ntr = floor_sum_result(a) * l + b;
  // Accumulate modulo 2^128: intermediate positive/negative contributions can
  // exceed signed 128 bits even when their exact sum satisfies the contract.
  using u128 = unsigned __int128;
  u128 res = 0;
  auto triangle = [](floor_sum_result cnt) -> u128 {
    u128 x = u128(cnt), y = cnt == 0 ? 0 : u128(cnt - 1);
    return x % 2 == 0 ? x / 2 * y : x * (y / 2);
  };

  floor_sum_result sq = floor_div(k, m);
  k -= sq * m;
  res += triangle(n) * u128(sq);
  floor_sum_result iq = floor_div(ntr, m);
  ntr -= iq * m;
  res += u128(n) * u128(iq);

  while (true) {
    if (k >= m) {
      res += triangle(n) * u128(k / m);
      k %= m;
    }
    if (ntr >= m) {
      res += u128(n) * u128(ntr / m);
      ntr %= m;
    }
    floor_sum_result mx = k * n + ntr;
    if (mx < m) {
      break;
    }
    n = mx / m;
    ntr = mx % m;
    std::swap(m, k);
  }
  return static_cast<floor_sum_result>(res);
}

} // namespace noya

#endif // NOYA_FLOOR_SUM_HPP
/// @complexity Time: O(log m).
/// Space: O(1).

#include <cassert>
#include <cstdint>
#include <utility>

namespace noya {

using floor_sum_result = __int128_t;

namespace floor_sum_internal {

inline floor_sum_result floor_div(floor_sum_result val, floor_sum_result mod) {
  floor_sum_result quo = val / mod;
  if (val % mod < 0) {
    quo--;
  }
  return quo;
}

} // namespace floor_sum_internal

/// @brief Sum floor((a*i+b)/mod) over i in [l, r), allowing signed
/// l, a, and b; the exact result must fit in signed 128 bits.
inline floor_sum_result floor_sum_range(std::int64_t l, std::int64_t r,
                                        std::int64_t a, std::int64_t b,
                                        std::int64_t mod) {
  assert(l <= r && mod > 0);
  using floor_sum_internal::floor_div;
  floor_sum_result n = floor_sum_result(r) - l;
  floor_sum_result m = mod;
  floor_sum_result k = a;
  floor_sum_result ntr = floor_sum_result(a) * l + b;
  // Accumulate modulo 2^128: intermediate positive/negative contributions can
  // exceed signed 128 bits even when their exact sum satisfies the contract.
  using u128 = unsigned __int128;
  u128 res = 0;
  auto triangle = [](floor_sum_result cnt) -> u128 {
    u128 x = u128(cnt), y = cnt == 0 ? 0 : u128(cnt - 1);
    return x % 2 == 0 ? x / 2 * y : x * (y / 2);
  };

  floor_sum_result sq = floor_div(k, m);
  k -= sq * m;
  res += triangle(n) * u128(sq);
  floor_sum_result iq = floor_div(ntr, m);
  ntr -= iq * m;
  res += u128(n) * u128(iq);

  while (true) {
    if (k >= m) {
      res += triangle(n) * u128(k / m);
      k %= m;
    }
    if (ntr >= m) {
      res += u128(n) * u128(ntr / m);
      ntr %= m;
    }
    floor_sum_result mx = k * n + ntr;
    if (mx < m) {
      break;
    }
    n = mx / m;
    ntr = mx % m;
    std::swap(m, k);
  }
  return static_cast<floor_sum_result>(res);
}

} // namespace noya

 // NOYA_FLOOR_SUM_HPP