Skip to content

static_range_lis_query.hpp

SECTIONData Structure INCLUDEnoya/static_range_lis_query.hpp

回答静态序列多个子区间的最长严格上升子序列长度,而不必为每个询问重新跑 LIS。

Complexity: Time: O(n log^2 n) preprocessing and O(log n) per query. Space: O(n log n).

AC 记录:static_range_lis_query

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n log^2 n) preprocessing and O(log n) per query.
/// Space: O(n log n).

#include <algorithm>
#include <cassert>
#include <climits>
#include <numeric>
#include <utility>
#include <vector>

namespace noya {

namespace static_range_lis_internal {

using uint = unsigned int;
using ll = long long;
static constexpr int wb = CHAR_BIT * sizeof(uint);

inline int popcount(uint val) {
#ifdef __GNUC__
  return __builtin_popcount(val);
#else
  static_assert(wb == 32);
  val -= val >> 1 & 0x55555555;
  val = (val & 0x33333333) + (val >> 2 & 0x33333333);
  val = val + (val >> 4) & 0x0f0f0f0f;
  return val * 0x01010101 >> 24 & 0x3f;
#endif
}

class bit_vector {
  struct node {
    uint bit = 0;
    int sum = 0;
  };
  std::vector<node> dat;

public:
  explicit bit_vector(uint n) : dat(n / wb + 1) {}

  void set(uint idx) {
    dat[idx / wb].bit |= uint(1) << idx % wb;
    dat[idx / wb].sum++;
  }

  void build() {
    for (int i = 1; i < int(dat.size()); i++) {
      dat[i].sum += dat[i - 1].sum;
    }
  }

  int rank(uint idx) const {
    return dat[idx / wb].sum -
           popcount(dat[idx / wb].bit & (~uint(0) << (idx % wb)));
  }

  int ones() const { return dat.back().sum; }
};

class wavelet_matrix {
  template <class Integer> static bool test(Integer val, int bit) {
    return (val & (Integer(1) << bit)) != Integer(0);
  }

  std::vector<bit_vector> lg;

public:
  template <class Integer>
  wavelet_matrix(int lg0, std::vector<Integer> a)
      : lg(lg0, bit_vector(a.size())) {
    int n = int(a.size());
    std::vector<Integer> z;
    z.reserve(n);
    for (int bit = lg0 - 1; bit >= 0; bit--) {
      bit_vector &dep = lg[bit];
      auto one = a.begin();
      for (int i = 0; i < n; i++) {
        if (test(a[i], bit)) {
          dep.set(i);
          *one++ = a[i];
        } else {
          z.push_back(a[i]);
        }
      }
      dep.build();
      std::copy(z.begin(), z.end(), one);
      z.clear();
    }
  }

  int count_less_than(int l, int r, ll key) const {
    int res = r - l;
    for (int bit = int(lg.size()) - 1; bit >= 0; bit--) {
      const bit_vector &dep = lg[bit];
      int rl = dep.rank(l);
      int rr = dep.rank(r);
      if (test(key, bit)) {
        l = rl;
        r = rr;
      } else {
        res -= rr - rl;
        int cnt = dep.ones();
        l += cnt - rl;
        r += cnt - rr;
      }
    }
    return res - (r - l);
  }
};

using permutation = std::vector<int>;
using iterator = permutation::iterator;
static constexpr int nil = -1;

inline permutation inverse(const permutation &p) {
  permutation res(p.size(), nil);
  for (int i = 0; i < int(p.size()); i++) {
    if (p[i] != nil) {
      res[p[i]] = i;
    }
  }
  return res;
}

inline void unit_monge_distance_product(int n, iterator stk, const iterator arr,
                                        const iterator b) {
  if (n == 1) {
    stk[0] = 0;
    return;
  }
  const iterator ro0 = stk;
  stk += n;
  const iterator col = stk;
  stk += n;

  const auto dfs = [=](int len, const auto &in, const auto &f) {
    const iterator ha = stk;
    const iterator ma = stk + len;
    const iterator hb = stk + 2 * len;
    const iterator mb = stk + 3 * len;
    const auto cut = [=](const iterator a, iterator hf, iterator map) {
      for (int i = 0; i < n; i++) {
        if (in(a[i])) {
          *hf++ = f(a[i]);
          *map++ = i;
        }
      }
    };
    cut(arr, ha, ma);
    cut(b, hb, mb);
    const iterator prd = stk + 4 * len;
    unit_monge_distance_product(len, prd, ha, hb);
    for (int i = 0; i < len; i++) {
      int row = ma[i];
      int y = mb[prd[i]];
      ro0[row] = y;
      col[y] = row;
    }
  };

  int mid = n / 2;
  dfs(mid, [mid](int val) { return val < mid; }, [](int val) { return val; });
  dfs(
      n - mid, [mid](int val) { return val >= mid; },
      [mid](int val) { return val - mid; });

  struct diagonal_iterator {
    int dif = 0;
    int y = 0;
  };
  int row = n;
  const auto dr = [&](diagonal_iterator &it) {
    if (b[it.y] < mid) {
      if (col[it.y] >= row) {
        it.dif++;
      }
    } else if (col[it.y] < row) {
      it.dif++;
    }
    it.y++;
  };
  const auto up = [&](diagonal_iterator &it) {
    if (arr[row] < mid) {
      if (ro0[row] >= it.y) {
        it.dif--;
      }
    } else if (ro0[row] < it.y) {
      it.dif--;
    }
  };

  diagonal_iterator neg, pos;
  while (row != 0) {
    while (pos.y != n) {
      diagonal_iterator can = pos;
      dr(can);
      if (can.dif != 0) {
        break;
      }
      pos = can;
    }
    row--;
    up(neg);
    up(pos);
    while (neg.dif != 0) {
      dr(neg);
    }
    if (neg.y > pos.y) {
      ro0[row] = pos.y;
    }
  }
}

inline permutation subunit_monge_distance_product(permutation arr,
                                                  permutation b) {
  int n = int(arr.size());
  permutation ia = inverse(arr);
  permutation ib = inverse(b);
  std::swap(b, ib);
  permutation ma, mb;
  for (int i = n - 1; i >= 0; i--) {
    if (arr[i] != nil) {
      ma.push_back(i);
      arr[n - int(ma.size())] = arr[i];
    }
  }
  std::reverse(ma.begin(), ma.end());
  {
    int num = 0;
    for (int i = 0; i < n; i++) {
      if (ia[i] == nil) {
        arr[num++] = i;
      }
    }
  }
  for (int i = 0; i < n; i++) {
    if (b[i] != nil) {
      b[mb.size()] = b[i];
      mb.push_back(i);
    }
  }
  {
    int num = int(mb.size());
    for (int i = 0; i < n; i++) {
      if (ib[i] == nil) {
        b[num++] = i;
      }
    }
  }

  permutation buf([](int len) {
    int sz = 0;
    while (len > 1) {
      sz += 2 * len;
      len = (len + 1) / 2;
      sz += 4 * len;
    }
    return sz + 1;
  }(n));
  unit_monge_distance_product(n, buf.begin(), arr.begin(), b.begin());

  permutation res(n, nil);
  for (int i = 0; i < int(ma.size()); i++) {
    int y = buf[n - int(ma.size()) + i];
    if (y < int(mb.size())) {
      res[ma[i]] = mb[y];
    }
  }
  return res;
}

inline permutation seaweed_doubling(const permutation &p) {
  int n = int(p.size());
  if (n == 1) {
    return {nil};
  }
  int mid = n / 2;
  permutation low, hi, ml, mh;
  for (int i = 0; i < n; i++) {
    if (p[i] < mid) {
      low.push_back(p[i]);
      ml.push_back(i);
    } else {
      hi.push_back(p[i] - mid);
      mh.push_back(i);
    }
  }
  low = seaweed_doubling(low);
  hi = seaweed_doubling(hi);
  permutation pl(n), ph(n);
  std::iota(pl.begin(), pl.end(), 0);
  std::iota(ph.begin(), ph.end(), 0);
  for (int i = 0; i < mid; i++) {
    pl[ml[i]] = low[i] == nil ? nil : ml[low[i]];
  }
  for (int i = 0; mid + i < n; i++) {
    ph[mh[i]] = hi[i] == nil ? nil : mh[hi[i]];
  }
  return subunit_monge_distance_product(std::move(pl), std::move(ph));
}

inline bool is_permutation(const permutation &p) {
  std::vector<bool> vis(p.size());
  for (int val : p) {
    if (val < 0 || val >= int(p.size()) || vis[val]) {
      return false;
    }
    vis[val] = true;
  }
  return true;
}

inline wavelet_matrix build_wavelet_matrix(const permutation &p) {
  assert(is_permutation(p));
  int n = int(p.size());
  permutation row;
  if (n != 0) {
    row = seaweed_doubling(p);
  }
  for (int &val : row) {
    if (val == nil) {
      val = n;
    }
  }
  int lg0 = 0;
  for (int val = n; val > 0; val /= 2) {
    lg0++;
  }
  return wavelet_matrix(lg0, std::move(row));
}

} // namespace static_range_lis_internal

/// @brief Answer LIS lengths on subarrays of a permutation.
/// Seaweed doubling represents semi-local LCS against the sorted permutation
/// as a subunit-Monge permutation.  Unit-Monge distance products merge the two
/// halves, and a wavelet matrix over the resulting critical points turns each
/// interval LIS into one orthogonal counting query.
class static_range_lis_query {
  int n;
  static_range_lis_internal::wavelet_matrix wm;

public:
  static_range_lis_query() : static_range_lis_query(std::vector<int>{}) {}
  explicit static_range_lis_query(const std::vector<int> &p0)
      : n(int(p0.size())),
        wm(static_range_lis_internal::build_wavelet_matrix(p0)) {}

  int query(int l, int r) const {
    assert(0 <= l && l <= r && r <= n);
    return (r - l) - wm.count_less_than(l, n, r);
  }
};

} // namespace noya
#ifndef NOYA_STATIC_RANGE_LIS_QUERY_HPP
#define NOYA_STATIC_RANGE_LIS_QUERY_HPP 1

/// @complexity Time: O(n log^2 n) preprocessing and O(log n) per query.
/// Space: O(n log n).

#include <algorithm>
#include <cassert>
#include <climits>
#include <numeric>
#include <utility>
#include <vector>

namespace noya {

namespace static_range_lis_internal {

using uint = unsigned int;
using ll = long long;
static constexpr int wb = CHAR_BIT * sizeof(uint);

inline int popcount(uint val) {
#ifdef __GNUC__
  return __builtin_popcount(val);
#else
  static_assert(wb == 32);
  val -= val >> 1 & 0x55555555;
  val = (val & 0x33333333) + (val >> 2 & 0x33333333);
  val = val + (val >> 4) & 0x0f0f0f0f;
  return val * 0x01010101 >> 24 & 0x3f;
#endif
}

class bit_vector {
  struct node {
    uint bit = 0;
    int sum = 0;
  };
  std::vector<node> dat;

public:
  explicit bit_vector(uint n) : dat(n / wb + 1) {}

  void set(uint idx) {
    dat[idx / wb].bit |= uint(1) << idx % wb;
    dat[idx / wb].sum++;
  }

  void build() {
    for (int i = 1; i < int(dat.size()); i++) {
      dat[i].sum += dat[i - 1].sum;
    }
  }

  int rank(uint idx) const {
    return dat[idx / wb].sum -
           popcount(dat[idx / wb].bit & (~uint(0) << (idx % wb)));
  }

  int ones() const { return dat.back().sum; }
};

class wavelet_matrix {
  template <class Integer> static bool test(Integer val, int bit) {
    return (val & (Integer(1) << bit)) != Integer(0);
  }

  std::vector<bit_vector> lg;

public:
  template <class Integer>
  wavelet_matrix(int lg0, std::vector<Integer> a)
      : lg(lg0, bit_vector(a.size())) {
    int n = int(a.size());
    std::vector<Integer> z;
    z.reserve(n);
    for (int bit = lg0 - 1; bit >= 0; bit--) {
      bit_vector &dep = lg[bit];
      auto one = a.begin();
      for (int i = 0; i < n; i++) {
        if (test(a[i], bit)) {
          dep.set(i);
          *one++ = a[i];
        } else {
          z.push_back(a[i]);
        }
      }
      dep.build();
      std::copy(z.begin(), z.end(), one);
      z.clear();
    }
  }

  int count_less_than(int l, int r, ll key) const {
    int res = r - l;
    for (int bit = int(lg.size()) - 1; bit >= 0; bit--) {
      const bit_vector &dep = lg[bit];
      int rl = dep.rank(l);
      int rr = dep.rank(r);
      if (test(key, bit)) {
        l = rl;
        r = rr;
      } else {
        res -= rr - rl;
        int cnt = dep.ones();
        l += cnt - rl;
        r += cnt - rr;
      }
    }
    return res - (r - l);
  }
};

using permutation = std::vector<int>;
using iterator = permutation::iterator;
static constexpr int nil = -1;

inline permutation inverse(const permutation &p) {
  permutation res(p.size(), nil);
  for (int i = 0; i < int(p.size()); i++) {
    if (p[i] != nil) {
      res[p[i]] = i;
    }
  }
  return res;
}

inline void unit_monge_distance_product(int n, iterator stk, const iterator arr,
                                        const iterator b) {
  if (n == 1) {
    stk[0] = 0;
    return;
  }
  const iterator ro0 = stk;
  stk += n;
  const iterator col = stk;
  stk += n;

  const auto dfs = [=](int len, const auto &in, const auto &f) {
    const iterator ha = stk;
    const iterator ma = stk + len;
    const iterator hb = stk + 2 * len;
    const iterator mb = stk + 3 * len;
    const auto cut = [=](const iterator a, iterator hf, iterator map) {
      for (int i = 0; i < n; i++) {
        if (in(a[i])) {
          *hf++ = f(a[i]);
          *map++ = i;
        }
      }
    };
    cut(arr, ha, ma);
    cut(b, hb, mb);
    const iterator prd = stk + 4 * len;
    unit_monge_distance_product(len, prd, ha, hb);
    for (int i = 0; i < len; i++) {
      int row = ma[i];
      int y = mb[prd[i]];
      ro0[row] = y;
      col[y] = row;
    }
  };

  int mid = n / 2;
  dfs(mid, [mid](int val) { return val < mid; }, [](int val) { return val; });
  dfs(
      n - mid, [mid](int val) { return val >= mid; },
      [mid](int val) { return val - mid; });

  struct diagonal_iterator {
    int dif = 0;
    int y = 0;
  };
  int row = n;
  const auto dr = [&](diagonal_iterator &it) {
    if (b[it.y] < mid) {
      if (col[it.y] >= row) {
        it.dif++;
      }
    } else if (col[it.y] < row) {
      it.dif++;
    }
    it.y++;
  };
  const auto up = [&](diagonal_iterator &it) {
    if (arr[row] < mid) {
      if (ro0[row] >= it.y) {
        it.dif--;
      }
    } else if (ro0[row] < it.y) {
      it.dif--;
    }
  };

  diagonal_iterator neg, pos;
  while (row != 0) {
    while (pos.y != n) {
      diagonal_iterator can = pos;
      dr(can);
      if (can.dif != 0) {
        break;
      }
      pos = can;
    }
    row--;
    up(neg);
    up(pos);
    while (neg.dif != 0) {
      dr(neg);
    }
    if (neg.y > pos.y) {
      ro0[row] = pos.y;
    }
  }
}

inline permutation subunit_monge_distance_product(permutation arr,
                                                  permutation b) {
  int n = int(arr.size());
  permutation ia = inverse(arr);
  permutation ib = inverse(b);
  std::swap(b, ib);
  permutation ma, mb;
  for (int i = n - 1; i >= 0; i--) {
    if (arr[i] != nil) {
      ma.push_back(i);
      arr[n - int(ma.size())] = arr[i];
    }
  }
  std::reverse(ma.begin(), ma.end());
  {
    int num = 0;
    for (int i = 0; i < n; i++) {
      if (ia[i] == nil) {
        arr[num++] = i;
      }
    }
  }
  for (int i = 0; i < n; i++) {
    if (b[i] != nil) {
      b[mb.size()] = b[i];
      mb.push_back(i);
    }
  }
  {
    int num = int(mb.size());
    for (int i = 0; i < n; i++) {
      if (ib[i] == nil) {
        b[num++] = i;
      }
    }
  }

  permutation buf([](int len) {
    int sz = 0;
    while (len > 1) {
      sz += 2 * len;
      len = (len + 1) / 2;
      sz += 4 * len;
    }
    return sz + 1;
  }(n));
  unit_monge_distance_product(n, buf.begin(), arr.begin(), b.begin());

  permutation res(n, nil);
  for (int i = 0; i < int(ma.size()); i++) {
    int y = buf[n - int(ma.size()) + i];
    if (y < int(mb.size())) {
      res[ma[i]] = mb[y];
    }
  }
  return res;
}

inline permutation seaweed_doubling(const permutation &p) {
  int n = int(p.size());
  if (n == 1) {
    return {nil};
  }
  int mid = n / 2;
  permutation low, hi, ml, mh;
  for (int i = 0; i < n; i++) {
    if (p[i] < mid) {
      low.push_back(p[i]);
      ml.push_back(i);
    } else {
      hi.push_back(p[i] - mid);
      mh.push_back(i);
    }
  }
  low = seaweed_doubling(low);
  hi = seaweed_doubling(hi);
  permutation pl(n), ph(n);
  std::iota(pl.begin(), pl.end(), 0);
  std::iota(ph.begin(), ph.end(), 0);
  for (int i = 0; i < mid; i++) {
    pl[ml[i]] = low[i] == nil ? nil : ml[low[i]];
  }
  for (int i = 0; mid + i < n; i++) {
    ph[mh[i]] = hi[i] == nil ? nil : mh[hi[i]];
  }
  return subunit_monge_distance_product(std::move(pl), std::move(ph));
}

inline bool is_permutation(const permutation &p) {
  std::vector<bool> vis(p.size());
  for (int val : p) {
    if (val < 0 || val >= int(p.size()) || vis[val]) {
      return false;
    }
    vis[val] = true;
  }
  return true;
}

inline wavelet_matrix build_wavelet_matrix(const permutation &p) {
  assert(is_permutation(p));
  int n = int(p.size());
  permutation row;
  if (n != 0) {
    row = seaweed_doubling(p);
  }
  for (int &val : row) {
    if (val == nil) {
      val = n;
    }
  }
  int lg0 = 0;
  for (int val = n; val > 0; val /= 2) {
    lg0++;
  }
  return wavelet_matrix(lg0, std::move(row));
}

} // namespace static_range_lis_internal

/// @brief Answer LIS lengths on subarrays of a permutation.
/// Seaweed doubling represents semi-local LCS against the sorted permutation
/// as a subunit-Monge permutation.  Unit-Monge distance products merge the two
/// halves, and a wavelet matrix over the resulting critical points turns each
/// interval LIS into one orthogonal counting query.
class static_range_lis_query {
  int n;
  static_range_lis_internal::wavelet_matrix wm;

public:
  static_range_lis_query() : static_range_lis_query(std::vector<int>{}) {}
  explicit static_range_lis_query(const std::vector<int> &p0)
      : n(int(p0.size())),
        wm(static_range_lis_internal::build_wavelet_matrix(p0)) {}

  int query(int l, int r) const {
    assert(0 <= l && l <= r && r <= n);
    return (r - l) - wm.count_less_than(l, n, r);
  }
};

} // namespace noya

#endif // NOYA_STATIC_RANGE_LIS_QUERY_HPP
#include <algorithm>
#include <cassert>
#include <climits>
#include <numeric>
#include <utility>
#include <vector>

/// @complexity Time: O(n log^2 n) preprocessing and O(log n) per query.
/// Space: O(n log n).

namespace noya {

namespace static_range_lis_internal {

using uint = unsigned int;
using ll = long long;
static constexpr int wb = CHAR_BIT * sizeof(uint);

inline int popcount(uint val) {
#ifdef __GNUC__
  return __builtin_popcount(val);
#else
  static_assert(wb == 32);
  val -= val >> 1 & 0x55555555;
  val = (val & 0x33333333) + (val >> 2 & 0x33333333);
  val = val + (val >> 4) & 0x0f0f0f0f;
  return val * 0x01010101 >> 24 & 0x3f;
#endif
}

class bit_vector {
  struct node {
    uint bit = 0;
    int sum = 0;
  };
  std::vector<node> dat;

public:
  explicit bit_vector(uint n) : dat(n / wb + 1) {}

  void set(uint idx) {
    dat[idx / wb].bit |= uint(1) << idx % wb;
    dat[idx / wb].sum++;
  }

  void build() {
    for (int i = 1; i < int(dat.size()); i++) {
      dat[i].sum += dat[i - 1].sum;
    }
  }

  int rank(uint idx) const {
    return dat[idx / wb].sum -
           popcount(dat[idx / wb].bit & (~uint(0) << (idx % wb)));
  }

  int ones() const { return dat.back().sum; }
};

class wavelet_matrix {
  template <class Integer> static bool test(Integer val, int bit) {
    return (val & (Integer(1) << bit)) != Integer(0);
  }

  std::vector<bit_vector> lg;

public:
  template <class Integer>
  wavelet_matrix(int lg0, std::vector<Integer> a)
      : lg(lg0, bit_vector(a.size())) {
    int n = int(a.size());
    std::vector<Integer> z;
    z.reserve(n);
    for (int bit = lg0 - 1; bit >= 0; bit--) {
      bit_vector &dep = lg[bit];
      auto one = a.begin();
      for (int i = 0; i < n; i++) {
        if (test(a[i], bit)) {
          dep.set(i);
          *one++ = a[i];
        } else {
          z.push_back(a[i]);
        }
      }
      dep.build();
      std::copy(z.begin(), z.end(), one);
      z.clear();
    }
  }

  int count_less_than(int l, int r, ll key) const {
    int res = r - l;
    for (int bit = int(lg.size()) - 1; bit >= 0; bit--) {
      const bit_vector &dep = lg[bit];
      int rl = dep.rank(l);
      int rr = dep.rank(r);
      if (test(key, bit)) {
        l = rl;
        r = rr;
      } else {
        res -= rr - rl;
        int cnt = dep.ones();
        l += cnt - rl;
        r += cnt - rr;
      }
    }
    return res - (r - l);
  }
};

using permutation = std::vector<int>;
using iterator = permutation::iterator;
static constexpr int nil = -1;

inline permutation inverse(const permutation &p) {
  permutation res(p.size(), nil);
  for (int i = 0; i < int(p.size()); i++) {
    if (p[i] != nil) {
      res[p[i]] = i;
    }
  }
  return res;
}

inline void unit_monge_distance_product(int n, iterator stk, const iterator arr,
                                        const iterator b) {
  if (n == 1) {
    stk[0] = 0;
    return;
  }
  const iterator ro0 = stk;
  stk += n;
  const iterator col = stk;
  stk += n;

  const auto dfs = [=](int len, const auto &in, const auto &f) {
    const iterator ha = stk;
    const iterator ma = stk + len;
    const iterator hb = stk + 2 * len;
    const iterator mb = stk + 3 * len;
    const auto cut = [=](const iterator a, iterator hf, iterator map) {
      for (int i = 0; i < n; i++) {
        if (in(a[i])) {
          *hf++ = f(a[i]);
          *map++ = i;
        }
      }
    };
    cut(arr, ha, ma);
    cut(b, hb, mb);
    const iterator prd = stk + 4 * len;
    unit_monge_distance_product(len, prd, ha, hb);
    for (int i = 0; i < len; i++) {
      int row = ma[i];
      int y = mb[prd[i]];
      ro0[row] = y;
      col[y] = row;
    }
  };

  int mid = n / 2;
  dfs(mid, [mid](int val) { return val < mid; }, [](int val) { return val; });
  dfs(
      n - mid, [mid](int val) { return val >= mid; },
      [mid](int val) { return val - mid; });

  struct diagonal_iterator {
    int dif = 0;
    int y = 0;
  };
  int row = n;
  const auto dr = [&](diagonal_iterator &it) {
    if (b[it.y] < mid) {
      if (col[it.y] >= row) {
        it.dif++;
      }
    } else if (col[it.y] < row) {
      it.dif++;
    }
    it.y++;
  };
  const auto up = [&](diagonal_iterator &it) {
    if (arr[row] < mid) {
      if (ro0[row] >= it.y) {
        it.dif--;
      }
    } else if (ro0[row] < it.y) {
      it.dif--;
    }
  };

  diagonal_iterator neg, pos;
  while (row != 0) {
    while (pos.y != n) {
      diagonal_iterator can = pos;
      dr(can);
      if (can.dif != 0) {
        break;
      }
      pos = can;
    }
    row--;
    up(neg);
    up(pos);
    while (neg.dif != 0) {
      dr(neg);
    }
    if (neg.y > pos.y) {
      ro0[row] = pos.y;
    }
  }
}

inline permutation subunit_monge_distance_product(permutation arr,
                                                  permutation b) {
  int n = int(arr.size());
  permutation ia = inverse(arr);
  permutation ib = inverse(b);
  std::swap(b, ib);
  permutation ma, mb;
  for (int i = n - 1; i >= 0; i--) {
    if (arr[i] != nil) {
      ma.push_back(i);
      arr[n - int(ma.size())] = arr[i];
    }
  }
  std::reverse(ma.begin(), ma.end());
  {
    int num = 0;
    for (int i = 0; i < n; i++) {
      if (ia[i] == nil) {
        arr[num++] = i;
      }
    }
  }
  for (int i = 0; i < n; i++) {
    if (b[i] != nil) {
      b[mb.size()] = b[i];
      mb.push_back(i);
    }
  }
  {
    int num = int(mb.size());
    for (int i = 0; i < n; i++) {
      if (ib[i] == nil) {
        b[num++] = i;
      }
    }
  }

  permutation buf([](int len) {
    int sz = 0;
    while (len > 1) {
      sz += 2 * len;
      len = (len + 1) / 2;
      sz += 4 * len;
    }
    return sz + 1;
  }(n));
  unit_monge_distance_product(n, buf.begin(), arr.begin(), b.begin());

  permutation res(n, nil);
  for (int i = 0; i < int(ma.size()); i++) {
    int y = buf[n - int(ma.size()) + i];
    if (y < int(mb.size())) {
      res[ma[i]] = mb[y];
    }
  }
  return res;
}

inline permutation seaweed_doubling(const permutation &p) {
  int n = int(p.size());
  if (n == 1) {
    return {nil};
  }
  int mid = n / 2;
  permutation low, hi, ml, mh;
  for (int i = 0; i < n; i++) {
    if (p[i] < mid) {
      low.push_back(p[i]);
      ml.push_back(i);
    } else {
      hi.push_back(p[i] - mid);
      mh.push_back(i);
    }
  }
  low = seaweed_doubling(low);
  hi = seaweed_doubling(hi);
  permutation pl(n), ph(n);
  std::iota(pl.begin(), pl.end(), 0);
  std::iota(ph.begin(), ph.end(), 0);
  for (int i = 0; i < mid; i++) {
    pl[ml[i]] = low[i] == nil ? nil : ml[low[i]];
  }
  for (int i = 0; mid + i < n; i++) {
    ph[mh[i]] = hi[i] == nil ? nil : mh[hi[i]];
  }
  return subunit_monge_distance_product(std::move(pl), std::move(ph));
}

inline bool is_permutation(const permutation &p) {
  std::vector<bool> vis(p.size());
  for (int val : p) {
    if (val < 0 || val >= int(p.size()) || vis[val]) {
      return false;
    }
    vis[val] = true;
  }
  return true;
}

inline wavelet_matrix build_wavelet_matrix(const permutation &p) {
  assert(is_permutation(p));
  int n = int(p.size());
  permutation row;
  if (n != 0) {
    row = seaweed_doubling(p);
  }
  for (int &val : row) {
    if (val == nil) {
      val = n;
    }
  }
  int lg0 = 0;
  for (int val = n; val > 0; val /= 2) {
    lg0++;
  }
  return wavelet_matrix(lg0, std::move(row));
}

} // namespace static_range_lis_internal

/// @brief Answer LIS lengths on subarrays of a permutation.
/// Seaweed doubling represents semi-local LCS against the sorted permutation
/// as a subunit-Monge permutation.  Unit-Monge distance products merge the two
/// halves, and a wavelet matrix over the resulting critical points turns each
/// interval LIS into one orthogonal counting query.
class static_range_lis_query {
  int n;
  static_range_lis_internal::wavelet_matrix wm;

public:
  static_range_lis_query() : static_range_lis_query(std::vector<int>{}) {}
  explicit static_range_lis_query(const std::vector<int> &p0)
      : n(int(p0.size())),
        wm(static_range_lis_internal::build_wavelet_matrix(p0)) {}

  int query(int l, int r) const {
    assert(0 <= l && l <= r && r <= n);
    return (r - l) - wm.count_less_than(l, n, r);
  }
};

} // namespace noya