cp-library

C++ Library for Competitive Programming

View the Project on GitHub emthrm/cp-library

:heavy_check_mark: Li Chao tree
(include/emthrm/dynamic_programming/li_chao_tree.hpp)

convex hull trick に対して、傾きに単調性のない直線または線分の追加を可能にするセグメント木である。

時間計算量

  時間計算量
初期化 $O(N \log{N})$
直線の追加クエリ $O(\log{N})$
線分の追加クエリ $O((\log{N})^2)$
解答クエリ $O(\log{N})$

仕様

template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree;

メンバ関数

名前 効果・戻り値 要件
explicit LiChaoTree(const std::vector<T>& xs_, const T inf); 解答クエリの $x$ 座標の集合を $\mathrm{xs}$ としてオブジェクトを構築する。  
void add(T a, T b); 直線 $f(x) = ax + b$ を追加する。  
void add(T a, T b, T left, T right); 線分 $f(x) = ax + b$ ($\mathrm{left} \leq x < \mathrm{right}$) を追加する。  
T query(const T x) const; $\min \text{/} \max \lbrace f(x) \mid f \in L \rbrace$ オフライン

メンバ型

名前 説明
Line 直線を表す構造体
struct Line;

メンバ変数

名前 説明
T a 傾き
T b 切片

メンバ関数

名前 効果・戻り値
explicit Line(const T a, const T b); 直線 $f(x) = ax + b$ を表すオブジェクトを構築する。
T f(const T x) const; $f(x)$

参考文献

TODO

Submissons

Verified with

Code

#ifndef EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_
#define EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_

#include <algorithm>
#include <bit>
#include <cassert>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>

namespace emthrm {

template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree {
  struct Line {
    T a, b;
    explicit Line(const T a, const T b) : a(a), b(b) {}
    T f(const T x) const { return a * x + b; }
  };

  explicit LiChaoTree(const std::vector<T>& xs_, const T inf) : n(1), xs(xs_) {
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    assert(xs.size() > 0);
    n = std::bit_ceil(xs.size());
    const T xs_back = xs.back();
    xs.resize(n, xs_back);
    dat.assign(n << 1, Line(0, inf));
  }

  void add(T a, T b) {
    if constexpr (!IS_MINIMIZED) {
      a = -a;
      b = -b;
    }
    Line line(a, b);
    add(&line, 1, 0, n);
  }

  void add(T a, T b, T left, T right) {
    if constexpr (!IS_MINIMIZED) {
      a = -a;
      b = -b;
    }
    for (int len = 1,
             node_l = std::distance(
                 xs.begin(), std::lower_bound(xs.begin(), xs.end(), left)),
             node_r = std::distance(
                 xs.begin(), std::lower_bound(xs.begin(), xs.end(), right)),
             l = node_l + n, r = node_r + n;
         l < r;
         l >>= 1, r >>= 1, len <<= 1) {
      if (l & 1) {
        Line line(a, b);
        add(&line, l++, node_l, node_l + len);
        node_l += len;
      }
      if (r & 1) {
        Line line(a, b);
        node_r -= len;
        add(&line, --r, node_r, node_r + len);
      }
    }
  }

  T query(const T x) const {
    int node = n + std::distance(xs.begin(),
                                 std::lower_bound(xs.begin(), xs.end(), x));
    T res = dat[node].f(x);
    while (node >>= 1) {
      if (dat[node].f(x) < res) res = dat[node].f(x);
    }
    return IS_MINIMIZED ? res : -res;
  }

 private:
  int n;
  std::vector<T> xs;
  std::vector<Line> dat;

  void add(Line* line, int node, int left, int right) {
    const bool flag_l = dat[node].f(xs[left]) <= line->f(xs[left]);
    const bool flag_r = dat[node].f(xs[right - 1]) <= line->f(xs[right - 1]);
    if (flag_l && flag_r) return;
    if (!flag_l && !flag_r) {
      std::swap(dat[node], *line);
      return;
    }
    const int mid = std::midpoint(left, right);
    if (line->f(xs[mid]) < dat[node].f(xs[mid])) std::swap(dat[node], *line);
    if (line->f(xs[left]) <= dat[node].f(xs[left])) {
      add(line, node << 1, left, mid);
    } else {
      add(line, (node << 1) + 1, mid, right);
    }
  }
};

}  // namespace emthrm

#endif  // EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_
#line 1 "include/emthrm/dynamic_programming/li_chao_tree.hpp"



#include <algorithm>
#include <bit>
#include <cassert>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>

namespace emthrm {

template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree {
  struct Line {
    T a, b;
    explicit Line(const T a, const T b) : a(a), b(b) {}
    T f(const T x) const { return a * x + b; }
  };

  explicit LiChaoTree(const std::vector<T>& xs_, const T inf) : n(1), xs(xs_) {
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    assert(xs.size() > 0);
    n = std::bit_ceil(xs.size());
    const T xs_back = xs.back();
    xs.resize(n, xs_back);
    dat.assign(n << 1, Line(0, inf));
  }

  void add(T a, T b) {
    if constexpr (!IS_MINIMIZED) {
      a = -a;
      b = -b;
    }
    Line line(a, b);
    add(&line, 1, 0, n);
  }

  void add(T a, T b, T left, T right) {
    if constexpr (!IS_MINIMIZED) {
      a = -a;
      b = -b;
    }
    for (int len = 1,
             node_l = std::distance(
                 xs.begin(), std::lower_bound(xs.begin(), xs.end(), left)),
             node_r = std::distance(
                 xs.begin(), std::lower_bound(xs.begin(), xs.end(), right)),
             l = node_l + n, r = node_r + n;
         l < r;
         l >>= 1, r >>= 1, len <<= 1) {
      if (l & 1) {
        Line line(a, b);
        add(&line, l++, node_l, node_l + len);
        node_l += len;
      }
      if (r & 1) {
        Line line(a, b);
        node_r -= len;
        add(&line, --r, node_r, node_r + len);
      }
    }
  }

  T query(const T x) const {
    int node = n + std::distance(xs.begin(),
                                 std::lower_bound(xs.begin(), xs.end(), x));
    T res = dat[node].f(x);
    while (node >>= 1) {
      if (dat[node].f(x) < res) res = dat[node].f(x);
    }
    return IS_MINIMIZED ? res : -res;
  }

 private:
  int n;
  std::vector<T> xs;
  std::vector<Line> dat;

  void add(Line* line, int node, int left, int right) {
    const bool flag_l = dat[node].f(xs[left]) <= line->f(xs[left]);
    const bool flag_r = dat[node].f(xs[right - 1]) <= line->f(xs[right - 1]);
    if (flag_l && flag_r) return;
    if (!flag_l && !flag_r) {
      std::swap(dat[node], *line);
      return;
    }
    const int mid = std::midpoint(left, right);
    if (line->f(xs[mid]) < dat[node].f(xs[mid])) std::swap(dat[node], *line);
    if (line->f(xs[left]) <= dat[node].f(xs[left])) {
      add(line, node << 1, left, mid);
    } else {
      add(line, (node << 1) + 1, mid, right);
    }
  }
};

}  // namespace emthrm
Back to top page