Skip to content

convex_hull_trick.hpp

SECTIONOptimization INCLUDEnoya/optimization/convex_hull_trick.hpp

支持单调 CHT、任意顺序的动态直线容器与二维点积最值;整数交点精确比较,点积查询可返回最优点编号。

Complexity: Time: amortized \(O(1)\) per monotone insertion/query; amortized \(O(\log n)\) per dynamic insertion and \(O(\log n)\) per dynamic/XY query. Space: \(O(n)\).

Source: maspy (line container and XY queries)

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: amortized O(1) per monotone insertion/query;
/// amortized O(log n) per dynamic insertion and O(log n) per dynamic/XY query.
/// Space: O(n).

#include <cassert>
#include <cstdint>
#include <limits>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>

namespace noya {
namespace cht_internal {
// Affine evaluations need twice the coefficient width. Unsigned products
// also fit in unsigned 128 bits after adding one 64-bit intercept.
template <class T>
using wide = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<std::is_signed_v<T>, __int128, unsigned __int128>, T>;

// Intersections have at most 64 magnitude bits in numerator/denominator.
// A zero denominator represents a signed infinity.
struct ratio {
  std::uint64_t num = 0, den = 1;
  bool neg = false;
  ratio(__int128 n = 0, __int128 d = 1)
      : num(n < 0 ? -n : n), den(d < 0 ? -d : d),
        neg(n != 0 && ((n < 0) != (d < 0))) {}
  ratio operator-() const {
    ratio r = *this;
    if (num) r.neg = !neg;
    return r;
  }
  bool operator<(const ratio &r) const {
    if (!den) return neg && (r.den || !r.neg);
    if (!r.den) return !r.neg;
    if (neg != r.neg) return neg;
    using U = unsigned __int128;
    U a = U(num) * r.den, b = U(r.num) * den;
    return neg ? a > b : a < b;
  }
};

template <class T, bool Fractional = true>
using point = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<Fractional, ratio, __int128>, T>;
} // namespace cht_internal

/// @brief Monotone convex-hull trick for decreasing slopes and queries.
/// Obsolete middle lines are removed by cross multiplication. Because query
/// coordinates are also decreasing, the current optimum moves monotonically
/// along the same stack and can be popped permanently. Integral coefficients
/// may use the full range of types up to 64 bits; query results must fit T.
template <class T> struct linear_cht_min {
  std::vector<std::pair<T, T>> stk;
  /// @brief Add line y = px + q. p must be decreasing.
  void add(T p, T q) {
    if (stk.size() >= 1) {
      assert(stk.back().first >= p);
    }
    if (stk.size() >= 1 && stk.back().first == p) {
      if (stk.back().second <= q) {
        return;
      } else {
        stk.pop_back();
      }
    }
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (keep_middle(p2, q2, p1, q1, p, q)) {
        break;
      }
      stk.pop_back();
    }
    stk.emplace_back(p, q);
  }
  /// @brief Query minimum at x. x must be decreasing.
  T get(T x) {
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (eval(p2, q2, x) > eval(p1, q1, x)) {
        break;
      } else {
        stk.pop_back();
      }
    }
    if (stk.empty()) {
      return std::numeric_limits<T>::max();
    } else {
      auto [k, b] = stk.back();
      return T(eval(k, b, x));
    }
  }

private:
  static cht_internal::wide<T> eval(T k, T b, T x) {
    return cht_internal::wide<T>(k) * x + b;
  }
  static bool keep_middle(T p2, T q2, T p1, T q1, T p, T q) {
    if constexpr (std::numeric_limits<T>::is_integer) {
      static_assert(sizeof(T) <= 8, "linear_cht_min supports up to 64-bit integers");
      __int128 a = __int128(q1) - __int128(q2);
      __int128 b = __int128(q) - __int128(q1);
      // Slopes are strictly decreasing here. Each difference has at most 64
      // magnitude bits, so its unsigned product fits even beyond signed 128.
      if ((a < 0) != (b < 0)) {
        return a < 0;
      }
      using U = unsigned __int128;
      U left = U(a < 0 ? -a : a) * U(__int128(p1) - __int128(p));
      U right = U(b < 0 ? -b : b) * U(__int128(p2) - __int128(p1));
      return a < 0 ? left > right : left < right;
    } else {
      using W = long double;
      return (W(q1) - W(q2)) * (W(p1) - W(p)) <
             (W(q) - W(q1)) * (W(p2) - W(p1));
    }
  }
};

template <typename T, bool Min = false, bool Fractional = false> struct Line {
  T k, m;
  mutable cht_internal::point<T, Fractional> p;
  int id = -1;
  bool operator<(const Line &o) const { return Min ? k > o.k : k < o.k; }
  bool operator<(const cht_internal::point<T, Fractional> &x) const { return p < x; }
};

template <typename T> T lc_inf() {
  if constexpr (std::is_floating_point_v<T>) return std::numeric_limits<T>::infinity();
  else return std::numeric_limits<T>::max();
}

template <typename T> T lc_div(T a, T b) {
  if constexpr (std::is_floating_point_v<T>) return a / b;
  else return a / b - ((a ^ b) < 0 && a % b);
}

/// @brief Dynamic line container with logarithmic insertion and queries.
/// Integer queries use floored intersections; Fractional retains exact ratios
/// for cht_xy, where rounding could discard a line optimal between integers.
/// Insertions use descending slopes for minima and ascending slopes for maxima.
/// Integral coefficients and coordinates may use up to 64 bits; results must fit T.
template <typename T, bool MIN = true, bool Fractional = false>
struct line_container : std::multiset<Line<T, MIN, Fractional>, std::less<>> {
  static_assert(!std::numeric_limits<T>::is_integer || sizeof(T) <= 8);
  using super = std::multiset<Line<T, MIN, Fractional>, std::less<>>;
  using point_type = cht_internal::point<T, Fractional>;
  using super::begin, super::end, super::insert, super::erase;
  using super::empty, super::lower_bound;
  point_type inf = [] {
    if constexpr (std::numeric_limits<T>::is_integer && Fractional) return point_type(1, 0);
    else return lc_inf<point_type>();
  }();
  bool insect(typename super::iterator x, typename super::iterator y) {
    if (y == end()) return x->p = inf, false;
    if (x->k == y->k) {
      bool better = MIN ? x->m < y->m : x->m > y->m;
      x->p = better ? inf : -inf;
    } else if constexpr (std::numeric_limits<T>::is_integer) {
      __int128 a = __int128(y->m) - x->m, b = __int128(x->k) - y->k;
      if constexpr (Fractional) x->p = point_type(a, b);
      else {
        // Differences have at most 64 magnitude bits, so a 64-bit division suffices.
        std::uint64_t u = a < 0 ? -a : a, v = b < 0 ? -b : b;
        __int128 q = u / v;
        x->p = (a < 0) != (b < 0) ? -q - (u % v != 0) : q;
      }
    } else {
      x->p = (y->m - x->m) / (x->k - y->k);
    }
    return !(x->p < y->p);
  }
  void add(T k, T m, int id = -1) {
    auto z = insert({k, m, {}, id}), y = z++, x = y;
    while (insect(y, z)) z = erase(z);
    if (x != begin() && insect(--x, y)) insect(x, y = erase(y));
    while ((y = x) != begin() && !((--x)->p < y->p)) insect(x, erase(y));
  }
  T query(T x) const {
    assert(!empty());
    auto l = lower_bound(point_type(x));
    return T(cht_internal::wide<T>(l->k) * x + l->m);
  }
};

template <typename T> using cht_min = line_container<T, true>;
template <typename T> using cht_max = line_container<T, false>;
/// @brief Maintain points (a,b), querying min/max a*x+b*y and an attaining id.
/// Integer directions and intersections stay exact; returned values must fit T.
template <typename T> struct cht_xy {
  static_assert(std::is_same_v<T, long long> || std::is_floating_point_v<T>);
  using C = std::conditional_t<std::is_floating_point_v<T>, long double, T>;
  using point_type = cht_internal::point<C>;
  using W = cht_internal::wide<T>;
  line_container<C, true, true> cm;
  line_container<C, false, true> cma;
  T bmx = std::numeric_limits<T>::lowest(), bmn = std::numeric_limits<T>::max();
  int jx = -1, jy = -1;
  bool emp = true;

  void clear() {
    emp = true;
    cm.clear();
    cma.clear();
    bmx = std::numeric_limits<T>::lowest();
    bmn = std::numeric_limits<T>::max();
    jx = jy = -1;
  }
  void add(T a, T b, int i = -1) {
    if (emp) {
      bmx = bmn = b;
      jx = jy = i;
    }
    emp = false;
    cm.add(b, a, i);
    cma.add(b, a, i);
    if (bmx < b)
      bmx = b, jx = i;
    if (bmn > b)
      bmn = b, jy = i;
  }

  std::pair<T, int> get_max(T x, T y) const { return get<false>(x, y); }
  std::pair<T, int> get_min(T x, T y) const { return get<true>(x, y); }

private:
  template <bool Min> std::pair<T, int> get(T x, T y) const {
    if (cm.empty())
      return {Min ? std::numeric_limits<T>::max() : std::numeric_limits<T>::lowest(), -1};
    if (x == 0) {
      bool upper = Min ? y < 0 : y > 0;
      return upper ? std::pair<T, int>{T(W(bmx) * y), jx}
                   : std::pair<T, int>{T(W(bmn) * y), jy};
    }
    point_type z;
    if constexpr (std::numeric_limits<T>::is_integer) z = point_type(y, x);
    else z = C(y) / x;
    auto eval = [&](auto it) {
      T a = it->m, b = it->k;
      return std::pair<T, int>{T(W(a) * x + W(b) * y), it->id};
    };
    if ((x > 0) == Min) return eval(cm.lower_bound(z));
    return eval(cma.lower_bound(z));
  }
};
} // namespace noya

 // NOYA_CONVEX_HULL_TRICK_HPP
#ifndef NOYA_CONVEX_HULL_TRICK_HPP
#define NOYA_CONVEX_HULL_TRICK_HPP 1

/// @complexity Time: amortized O(1) per monotone insertion/query;
/// amortized O(log n) per dynamic insertion and O(log n) per dynamic/XY query.
/// Space: O(n).

#include <cassert>
#include <cstdint>
#include <limits>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>

namespace noya {
namespace cht_internal {
// Affine evaluations need twice the coefficient width. Unsigned products
// also fit in unsigned 128 bits after adding one 64-bit intercept.
template <class T>
using wide = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<std::is_signed_v<T>, __int128, unsigned __int128>, T>;

// Intersections have at most 64 magnitude bits in numerator/denominator.
// A zero denominator represents a signed infinity.
struct ratio {
  std::uint64_t num = 0, den = 1;
  bool neg = false;
  ratio(__int128 n = 0, __int128 d = 1)
      : num(n < 0 ? -n : n), den(d < 0 ? -d : d),
        neg(n != 0 && ((n < 0) != (d < 0))) {}
  ratio operator-() const {
    ratio r = *this;
    if (num) r.neg = !neg;
    return r;
  }
  bool operator<(const ratio &r) const {
    if (!den) return neg && (r.den || !r.neg);
    if (!r.den) return !r.neg;
    if (neg != r.neg) return neg;
    using U = unsigned __int128;
    U a = U(num) * r.den, b = U(r.num) * den;
    return neg ? a > b : a < b;
  }
};

template <class T, bool Fractional = true>
using point = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<Fractional, ratio, __int128>, T>;
} // namespace cht_internal

/// @brief Monotone convex-hull trick for decreasing slopes and queries.
/// Obsolete middle lines are removed by cross multiplication. Because query
/// coordinates are also decreasing, the current optimum moves monotonically
/// along the same stack and can be popped permanently. Integral coefficients
/// may use the full range of types up to 64 bits; query results must fit T.
template <class T> struct linear_cht_min {
  std::vector<std::pair<T, T>> stk;
  /// @brief Add line y = px + q. p must be decreasing.
  void add(T p, T q) {
    if (stk.size() >= 1) {
      assert(stk.back().first >= p);
    }
    if (stk.size() >= 1 && stk.back().first == p) {
      if (stk.back().second <= q) {
        return;
      } else {
        stk.pop_back();
      }
    }
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (keep_middle(p2, q2, p1, q1, p, q)) {
        break;
      }
      stk.pop_back();
    }
    stk.emplace_back(p, q);
  }
  /// @brief Query minimum at x. x must be decreasing.
  T get(T x) {
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (eval(p2, q2, x) > eval(p1, q1, x)) {
        break;
      } else {
        stk.pop_back();
      }
    }
    if (stk.empty()) {
      return std::numeric_limits<T>::max();
    } else {
      auto [k, b] = stk.back();
      return T(eval(k, b, x));
    }
  }

private:
  static cht_internal::wide<T> eval(T k, T b, T x) {
    return cht_internal::wide<T>(k) * x + b;
  }
  static bool keep_middle(T p2, T q2, T p1, T q1, T p, T q) {
    if constexpr (std::numeric_limits<T>::is_integer) {
      static_assert(sizeof(T) <= 8, "linear_cht_min supports up to 64-bit integers");
      __int128 a = __int128(q1) - __int128(q2);
      __int128 b = __int128(q) - __int128(q1);
      // Slopes are strictly decreasing here. Each difference has at most 64
      // magnitude bits, so its unsigned product fits even beyond signed 128.
      if ((a < 0) != (b < 0)) {
        return a < 0;
      }
      using U = unsigned __int128;
      U left = U(a < 0 ? -a : a) * U(__int128(p1) - __int128(p));
      U right = U(b < 0 ? -b : b) * U(__int128(p2) - __int128(p1));
      return a < 0 ? left > right : left < right;
    } else {
      using W = long double;
      return (W(q1) - W(q2)) * (W(p1) - W(p)) <
             (W(q) - W(q1)) * (W(p2) - W(p1));
    }
  }
};

template <typename T, bool Min = false, bool Fractional = false> struct Line {
  T k, m;
  mutable cht_internal::point<T, Fractional> p;
  int id = -1;
  bool operator<(const Line &o) const { return Min ? k > o.k : k < o.k; }
  bool operator<(const cht_internal::point<T, Fractional> &x) const { return p < x; }
};

template <typename T> T lc_inf() {
  if constexpr (std::is_floating_point_v<T>) return std::numeric_limits<T>::infinity();
  else return std::numeric_limits<T>::max();
}

template <typename T> T lc_div(T a, T b) {
  if constexpr (std::is_floating_point_v<T>) return a / b;
  else return a / b - ((a ^ b) < 0 && a % b);
}

/// @brief Dynamic line container with logarithmic insertion and queries.
/// Integer queries use floored intersections; Fractional retains exact ratios
/// for cht_xy, where rounding could discard a line optimal between integers.
/// Insertions use descending slopes for minima and ascending slopes for maxima.
/// Integral coefficients and coordinates may use up to 64 bits; results must fit T.
template <typename T, bool MIN = true, bool Fractional = false>
struct line_container : std::multiset<Line<T, MIN, Fractional>, std::less<>> {
  static_assert(!std::numeric_limits<T>::is_integer || sizeof(T) <= 8);
  using super = std::multiset<Line<T, MIN, Fractional>, std::less<>>;
  using point_type = cht_internal::point<T, Fractional>;
  using super::begin, super::end, super::insert, super::erase;
  using super::empty, super::lower_bound;
  point_type inf = [] {
    if constexpr (std::numeric_limits<T>::is_integer && Fractional) return point_type(1, 0);
    else return lc_inf<point_type>();
  }();
  bool insect(typename super::iterator x, typename super::iterator y) {
    if (y == end()) return x->p = inf, false;
    if (x->k == y->k) {
      bool better = MIN ? x->m < y->m : x->m > y->m;
      x->p = better ? inf : -inf;
    } else if constexpr (std::numeric_limits<T>::is_integer) {
      __int128 a = __int128(y->m) - x->m, b = __int128(x->k) - y->k;
      if constexpr (Fractional) x->p = point_type(a, b);
      else {
        // Differences have at most 64 magnitude bits, so a 64-bit division suffices.
        std::uint64_t u = a < 0 ? -a : a, v = b < 0 ? -b : b;
        __int128 q = u / v;
        x->p = (a < 0) != (b < 0) ? -q - (u % v != 0) : q;
      }
    } else {
      x->p = (y->m - x->m) / (x->k - y->k);
    }
    return !(x->p < y->p);
  }
  void add(T k, T m, int id = -1) {
    auto z = insert({k, m, {}, id}), y = z++, x = y;
    while (insect(y, z)) z = erase(z);
    if (x != begin() && insect(--x, y)) insect(x, y = erase(y));
    while ((y = x) != begin() && !((--x)->p < y->p)) insect(x, erase(y));
  }
  T query(T x) const {
    assert(!empty());
    auto l = lower_bound(point_type(x));
    return T(cht_internal::wide<T>(l->k) * x + l->m);
  }
};

template <typename T> using cht_min = line_container<T, true>;
template <typename T> using cht_max = line_container<T, false>;
/// @brief Maintain points (a,b), querying min/max a*x+b*y and an attaining id.
/// Integer directions and intersections stay exact; returned values must fit T.
template <typename T> struct cht_xy {
  static_assert(std::is_same_v<T, long long> || std::is_floating_point_v<T>);
  using C = std::conditional_t<std::is_floating_point_v<T>, long double, T>;
  using point_type = cht_internal::point<C>;
  using W = cht_internal::wide<T>;
  line_container<C, true, true> cm;
  line_container<C, false, true> cma;
  T bmx = std::numeric_limits<T>::lowest(), bmn = std::numeric_limits<T>::max();
  int jx = -1, jy = -1;
  bool emp = true;

  void clear() {
    emp = true;
    cm.clear();
    cma.clear();
    bmx = std::numeric_limits<T>::lowest();
    bmn = std::numeric_limits<T>::max();
    jx = jy = -1;
  }
  void add(T a, T b, int i = -1) {
    if (emp) {
      bmx = bmn = b;
      jx = jy = i;
    }
    emp = false;
    cm.add(b, a, i);
    cma.add(b, a, i);
    if (bmx < b)
      bmx = b, jx = i;
    if (bmn > b)
      bmn = b, jy = i;
  }

  std::pair<T, int> get_max(T x, T y) const { return get<false>(x, y); }
  std::pair<T, int> get_min(T x, T y) const { return get<true>(x, y); }

private:
  template <bool Min> std::pair<T, int> get(T x, T y) const {
    if (cm.empty())
      return {Min ? std::numeric_limits<T>::max() : std::numeric_limits<T>::lowest(), -1};
    if (x == 0) {
      bool upper = Min ? y < 0 : y > 0;
      return upper ? std::pair<T, int>{T(W(bmx) * y), jx}
                   : std::pair<T, int>{T(W(bmn) * y), jy};
    }
    point_type z;
    if constexpr (std::numeric_limits<T>::is_integer) z = point_type(y, x);
    else z = C(y) / x;
    auto eval = [&](auto it) {
      T a = it->m, b = it->k;
      return std::pair<T, int>{T(W(a) * x + W(b) * y), it->id};
    };
    if ((x > 0) == Min) return eval(cm.lower_bound(z));
    return eval(cma.lower_bound(z));
  }
};
} // namespace noya

#endif // NOYA_CONVEX_HULL_TRICK_HPP
/// @complexity Time: amortized O(1) per monotone insertion/query;
/// amortized O(log n) per dynamic insertion and O(log n) per dynamic/XY query.
/// Space: O(n).

#include <cassert>
#include <cstdint>
#include <limits>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>

namespace noya {
namespace cht_internal {
// Affine evaluations need twice the coefficient width. Unsigned products
// also fit in unsigned 128 bits after adding one 64-bit intercept.
template <class T>
using wide = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<std::is_signed_v<T>, __int128, unsigned __int128>, T>;

// Intersections have at most 64 magnitude bits in numerator/denominator.
// A zero denominator represents a signed infinity.
struct ratio {
  std::uint64_t num = 0, den = 1;
  bool neg = false;
  ratio(__int128 n = 0, __int128 d = 1)
      : num(n < 0 ? -n : n), den(d < 0 ? -d : d),
        neg(n != 0 && ((n < 0) != (d < 0))) {}
  ratio operator-() const {
    ratio r = *this;
    if (num) r.neg = !neg;
    return r;
  }
  bool operator<(const ratio &r) const {
    if (!den) return neg && (r.den || !r.neg);
    if (!r.den) return !r.neg;
    if (neg != r.neg) return neg;
    using U = unsigned __int128;
    U a = U(num) * r.den, b = U(r.num) * den;
    return neg ? a > b : a < b;
  }
};

template <class T, bool Fractional = true>
using point = std::conditional_t<std::numeric_limits<T>::is_integer,
    std::conditional_t<Fractional, ratio, __int128>, T>;
} // namespace cht_internal

/// @brief Monotone convex-hull trick for decreasing slopes and queries.
/// Obsolete middle lines are removed by cross multiplication. Because query
/// coordinates are also decreasing, the current optimum moves monotonically
/// along the same stack and can be popped permanently. Integral coefficients
/// may use the full range of types up to 64 bits; query results must fit T.
template <class T> struct linear_cht_min {
  std::vector<std::pair<T, T>> stk;
  /// @brief Add line y = px + q. p must be decreasing.
  void add(T p, T q) {
    if (stk.size() >= 1) {
      assert(stk.back().first >= p);
    }
    if (stk.size() >= 1 && stk.back().first == p) {
      if (stk.back().second <= q) {
        return;
      } else {
        stk.pop_back();
      }
    }
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (keep_middle(p2, q2, p1, q1, p, q)) {
        break;
      }
      stk.pop_back();
    }
    stk.emplace_back(p, q);
  }
  /// @brief Query minimum at x. x must be decreasing.
  T get(T x) {
    while ((int)stk.size() >= 2) {
      const auto [p2, q2] = stk.end()[-2];
      const auto [p1, q1] = stk.end()[-1];
      if (eval(p2, q2, x) > eval(p1, q1, x)) {
        break;
      } else {
        stk.pop_back();
      }
    }
    if (stk.empty()) {
      return std::numeric_limits<T>::max();
    } else {
      auto [k, b] = stk.back();
      return T(eval(k, b, x));
    }
  }

private:
  static cht_internal::wide<T> eval(T k, T b, T x) {
    return cht_internal::wide<T>(k) * x + b;
  }
  static bool keep_middle(T p2, T q2, T p1, T q1, T p, T q) {
    if constexpr (std::numeric_limits<T>::is_integer) {
      static_assert(sizeof(T) <= 8, "linear_cht_min supports up to 64-bit integers");
      __int128 a = __int128(q1) - __int128(q2);
      __int128 b = __int128(q) - __int128(q1);
      // Slopes are strictly decreasing here. Each difference has at most 64
      // magnitude bits, so its unsigned product fits even beyond signed 128.
      if ((a < 0) != (b < 0)) {
        return a < 0;
      }
      using U = unsigned __int128;
      U left = U(a < 0 ? -a : a) * U(__int128(p1) - __int128(p));
      U right = U(b < 0 ? -b : b) * U(__int128(p2) - __int128(p1));
      return a < 0 ? left > right : left < right;
    } else {
      using W = long double;
      return (W(q1) - W(q2)) * (W(p1) - W(p)) <
             (W(q) - W(q1)) * (W(p2) - W(p1));
    }
  }
};

template <typename T, bool Min = false, bool Fractional = false> struct Line {
  T k, m;
  mutable cht_internal::point<T, Fractional> p;
  int id = -1;
  bool operator<(const Line &o) const { return Min ? k > o.k : k < o.k; }
  bool operator<(const cht_internal::point<T, Fractional> &x) const { return p < x; }
};

template <typename T> T lc_inf() {
  if constexpr (std::is_floating_point_v<T>) return std::numeric_limits<T>::infinity();
  else return std::numeric_limits<T>::max();
}

template <typename T> T lc_div(T a, T b) {
  if constexpr (std::is_floating_point_v<T>) return a / b;
  else return a / b - ((a ^ b) < 0 && a % b);
}

/// @brief Dynamic line container with logarithmic insertion and queries.
/// Integer queries use floored intersections; Fractional retains exact ratios
/// for cht_xy, where rounding could discard a line optimal between integers.
/// Insertions use descending slopes for minima and ascending slopes for maxima.
/// Integral coefficients and coordinates may use up to 64 bits; results must fit T.
template <typename T, bool MIN = true, bool Fractional = false>
struct line_container : std::multiset<Line<T, MIN, Fractional>, std::less<>> {
  static_assert(!std::numeric_limits<T>::is_integer || sizeof(T) <= 8);
  using super = std::multiset<Line<T, MIN, Fractional>, std::less<>>;
  using point_type = cht_internal::point<T, Fractional>;
  using super::begin, super::end, super::insert, super::erase;
  using super::empty, super::lower_bound;
  point_type inf = [] {
    if constexpr (std::numeric_limits<T>::is_integer && Fractional) return point_type(1, 0);
    else return lc_inf<point_type>();
  }();
  bool insect(typename super::iterator x, typename super::iterator y) {
    if (y == end()) return x->p = inf, false;
    if (x->k == y->k) {
      bool better = MIN ? x->m < y->m : x->m > y->m;
      x->p = better ? inf : -inf;
    } else if constexpr (std::numeric_limits<T>::is_integer) {
      __int128 a = __int128(y->m) - x->m, b = __int128(x->k) - y->k;
      if constexpr (Fractional) x->p = point_type(a, b);
      else {
        // Differences have at most 64 magnitude bits, so a 64-bit division suffices.
        std::uint64_t u = a < 0 ? -a : a, v = b < 0 ? -b : b;
        __int128 q = u / v;
        x->p = (a < 0) != (b < 0) ? -q - (u % v != 0) : q;
      }
    } else {
      x->p = (y->m - x->m) / (x->k - y->k);
    }
    return !(x->p < y->p);
  }
  void add(T k, T m, int id = -1) {
    auto z = insert({k, m, {}, id}), y = z++, x = y;
    while (insect(y, z)) z = erase(z);
    if (x != begin() && insect(--x, y)) insect(x, y = erase(y));
    while ((y = x) != begin() && !((--x)->p < y->p)) insect(x, erase(y));
  }
  T query(T x) const {
    assert(!empty());
    auto l = lower_bound(point_type(x));
    return T(cht_internal::wide<T>(l->k) * x + l->m);
  }
};

template <typename T> using cht_min = line_container<T, true>;
template <typename T> using cht_max = line_container<T, false>;
/// @brief Maintain points (a,b), querying min/max a*x+b*y and an attaining id.
/// Integer directions and intersections stay exact; returned values must fit T.
template <typename T> struct cht_xy {
  static_assert(std::is_same_v<T, long long> || std::is_floating_point_v<T>);
  using C = std::conditional_t<std::is_floating_point_v<T>, long double, T>;
  using point_type = cht_internal::point<C>;
  using W = cht_internal::wide<T>;
  line_container<C, true, true> cm;
  line_container<C, false, true> cma;
  T bmx = std::numeric_limits<T>::lowest(), bmn = std::numeric_limits<T>::max();
  int jx = -1, jy = -1;
  bool emp = true;

  void clear() {
    emp = true;
    cm.clear();
    cma.clear();
    bmx = std::numeric_limits<T>::lowest();
    bmn = std::numeric_limits<T>::max();
    jx = jy = -1;
  }
  void add(T a, T b, int i = -1) {
    if (emp) {
      bmx = bmn = b;
      jx = jy = i;
    }
    emp = false;
    cm.add(b, a, i);
    cma.add(b, a, i);
    if (bmx < b)
      bmx = b, jx = i;
    if (bmn > b)
      bmn = b, jy = i;
  }

  std::pair<T, int> get_max(T x, T y) const { return get<false>(x, y); }
  std::pair<T, int> get_min(T x, T y) const { return get<true>(x, y); }

private:
  template <bool Min> std::pair<T, int> get(T x, T y) const {
    if (cm.empty())
      return {Min ? std::numeric_limits<T>::max() : std::numeric_limits<T>::lowest(), -1};
    if (x == 0) {
      bool upper = Min ? y < 0 : y > 0;
      return upper ? std::pair<T, int>{T(W(bmx) * y), jx}
                   : std::pair<T, int>{T(W(bmn) * y), jy};
    }
    point_type z;
    if constexpr (std::numeric_limits<T>::is_integer) z = point_type(y, x);
    else z = C(y) / x;
    auto eval = [&](auto it) {
      T a = it->m, b = it->k;
      return std::pair<T, int>{T(W(a) * x + W(b) * y), it->id};
    };
    if ((x > 0) == Min) return eval(cm.lower_bound(z));
    return eval(cma.lower_bound(z));
  }
};
} // namespace noya

 // NOYA_CONVEX_HULL_TRICK_HPP