概要

Li-Chao (Segment) Treeはセグメント木を用いてConvex Hull Trickを行うアルゴリズム。

詳細はこちら(ブログ): Li Chao Treeのメモ

計算量

  • 直線追加: \(O(\log N)\)

  • 線分追加: \(O(\log^2 N)\)

  • 最小値計算: \(O(\log N)\)

実装

#include<algorithm>
using namespace std;
using ll = long long;


#define N 300004

class LiChaoTree {
  static const ll inf = 1e9;
  static const ll infll = 1e18;

  int n;
  ll xs[4*N];
  ll p[4*N], q[4*N];
  bool u[4*N];

  void _add_line(ll a, ll b, int k, int l, int r) {
    while(r-l > 0) {
      int m = (l + r) >> 1;
      if(!u[k]) {
        p[k] = a; q[k] = b;
        u[k] = true;
        return;
      }

      ll lx = xs[l], mx = xs[m], rx = xs[r-1];
      ll pk = p[k], qk = q[k];
      bool left = (a*lx+b < pk*lx+qk);
      bool mid = (a*mx+b < pk*mx+qk);
      bool right = (a*rx+b < pk*rx+qk);
      if(left && right) {
        p[k] = a; q[k] = b;
        return;
      }
      if(!left && !right) {
        return;
      }
      if(mid) {
        swap(p[k], a);
        swap(q[k], b);
      }
      if(left != mid) {
        k = 2*k+1; r = m;
      } else {
        k = 2*k+2; l = m;
      }
    }
  }

  ll _query(int k, ll x) {
    k += n - 1;
    ll s = u[k] ? p[k]*x+q[k] : infll;
    while(k > 0) {
      k = (k - 1) / 2;
      if(u[k]) {
        ll r = p[k]*x+q[k];
        s = min(s, r);
      }
    }
    return s;
  }

public:
  LiChaoTree(int n0, ll ps[N]) {
    n = 1;
    while(n < n0) n <<= 1;

    for(int i=0; i<2*n; ++i) u[i] = false;
    for(int i=0; i<n0; ++i) xs[i] = ps[i];
    for(int i=n0; i<2*n-1; ++i) xs[i] = inf;
  }

  void add_line(ll a, ll b) {
    _add_line(a, b, 0, 0, n);
  }

  void add_segment_line(ll a, ll b, int l, int r) {
    ll l0 = l + n, r0 = r + n;
    ll s0 = l, t0 = r, sz = 1;
    while(l0 < r0) {
      if(r0 & 1) {
        --r0; t0 -= sz;
        _add_line(a, b, r0-1, t0, t0+sz);
      }
      if(l0 & 1) {
        _add_line(a, b, l0-1, s0, s0+sz);
        ++l0; s0 += sz;
      }
      l0 >>= 1; r0 >>= 1;
      sz <<= 1;
    }
  }

  ll query(int i) {
    return _query(i, xs[i]);
  }
};

Verified

直線処理

  • yukicoder: "No.703 ゴミ拾い Easy": source (C++14, 514ms)

  • AtCoder: "全国統一プログラミング王決定戦本戦 - G問題: Greatest Jorney": source (C++14, 1485ms)

線分処理

  • yukicoder: "No.704 ゴミ拾い Medium": source (C++14, 711ms)