Skip to content

common_interval_decomposition_tree.hpp

SECTIONData Structure INCLUDEnoya/common_interval_decomposition_tree.hpp

对两个排列构造公共区间树:若一组元素在两排列中分别对应连续下标段则称其为公共区间,树按该 laminar 家族分治分解并给出所有极大公共区间。

Complexity: Time: O(n log n). Space: O(n).

AC 记录:common_interval_decomposition_tree

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n log n).
/// Space: O(n).

#include "noya/lazy_segtree.hpp"

#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

namespace noya {

struct common_interval_node {
  int l = 0;
  int r = 0;
  int mn = 0;
  int mx = 0;
  bool lin = true;
  std::vector<int> ch;
};

struct common_interval_tree {
  int rt = -1;
  std::vector<common_interval_node> tr;
};

namespace common_interval_internal {

struct maximum_monoid {
  using value_type = int;
  static value_type unit() { return std::numeric_limits<int>::lowest() / 4; }
  static value_type op(value_type a, value_type b) { return std::max(a, b); }
};

struct addition_action {
  using value_type = int;
  static value_type unit() { return 0; }
  static value_type composition(value_type nw, value_type old) {
    return nw + old;
  }
  static int apply(value_type add, int val) { return val + add; }
};

} // namespace common_interval_internal

/// @brief Build the strong-interval Hasse tree of a permutation.
/// For every possible left endpoint, a lazy segment tree maintains
/// `max-min-length+1` for the suffix ending at the current position. Monotone
/// min/max stacks update this value on exactly the ranges whose extrema
/// change, so value zero detects a new common interval. A second stack merges
/// adjacent value ranges into linear nodes; otherwise all nodes up to the
/// detected boundary form one prime node. Every merge is permanent, giving a
/// laminar tree containing exactly the strong intervals.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int sz = int(p.size());
  assert(sz > 0);
  std::vector<bool> vis(sz);
  for (int val : p) {
    assert(0 <= val && val < sz);
    assert(!vis[val]);
    vis[val] = true;
  }

  using segment_tree = lazy_segtree<common_interval_internal::maximum_monoid,
                                    common_interval_internal::addition_action>;
  segment_tree seg(sz);
  std::vector<int> smi;
  std::vector<int> smx;
  std::vector<int> st;
  common_interval_tree res;
  res.tr.reserve(sz * 2 - 1);

  auto ins = [&](common_interval_node u) {
    res.tr.push_back(std::move(u));
    return int(res.tr.size()) - 1;
  };
  auto adj = [&](int a, int b) {
    return res.tr[a].mn == res.tr[b].mx || res.tr[a].mx == res.tr[b].mn;
  };

  for (int idx = 0; idx < sz; idx++) {
    int r = idx;
    while (!smi.empty() && p[smi.back()] > p[idx]) {
      smi.pop_back();
      int l = smi.empty() ? 0 : smi.back() + 1;
      seg.apply(l, r, p[idx] - p[r - 1]);
      r = l;
    }
    smi.push_back(idx);

    r = idx;
    while (!smx.empty() && p[smx.back()] < p[idx]) {
      smx.pop_back();
      int l = smx.empty() ? 0 : smx.back() + 1;
      seg.apply(l, r, -p[idx] + p[r - 1]);
      r = l;
    }
    smx.push_back(idx);
    seg.apply(0, idx, 1);

    int cur =
        ins(common_interval_node{idx, idx + 1, p[idx], p[idx] + 1, true, {}});
    while (true) {
      if (!st.empty()) {
        int top = st.back();
        if (res.tr[top].lin && !res.tr[top].ch.empty() &&
            adj(cur, res.tr[top].ch.back())) {
          st.pop_back();
          res.tr[top].ch.push_back(cur);
          res.tr[top].l = std::min(res.tr[top].l, res.tr[cur].l);
          res.tr[top].r = std::max(res.tr[top].r, res.tr[cur].r);
          res.tr[top].mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          res.tr[top].mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          cur = top;
          continue;
        }
        if (adj(top, cur)) {
          st.pop_back();
          common_interval_node tmp;
          tmp.l = res.tr[top].l;
          tmp.r = res.tr[cur].r;
          tmp.mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          tmp.mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          tmp.lin = true;
          tmp.ch = {top, cur};
          cur = ins(std::move(tmp));
          continue;
        }
      }

      st.push_back(cur);
      if (st.size() == 1) {
        break;
      }
      int l = res.tr[st.back()].l;
      if (seg.prod(0, l) != 0) {
        break;
      }

      int top = st.back();
      st.pop_back();
      common_interval_node tm = res.tr[top];
      tm.lin = false;
      tm.ch = {top};
      cur = ins(std::move(tm));
      do {
        assert(!st.empty());
        top = st.back();
        st.pop_back();
        res.tr[cur].ch.push_back(top);
        res.tr[cur].l = std::min(res.tr[cur].l, res.tr[top].l);
        res.tr[cur].r = std::max(res.tr[cur].r, res.tr[top].r);
        res.tr[cur].mn = std::min(res.tr[cur].mn, res.tr[top].mn);
        res.tr[cur].mx = std::max(res.tr[cur].mx, res.tr[top].mx);
      } while (res.tr[cur].r - res.tr[cur].l !=
               res.tr[cur].mx - res.tr[cur].mn);
      std::reverse(res.tr[cur].ch.begin(), res.tr[cur].ch.end());
    }
    seg.set(idx, 0);
  }
  assert(st.size() == 1);
  res.rt = st.back();
  return res;
}

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

/// @complexity Time: O(n log n).
/// Space: O(n).

#include "noya/lazy_segtree.hpp"

#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

namespace noya {

struct common_interval_node {
  int l = 0;
  int r = 0;
  int mn = 0;
  int mx = 0;
  bool lin = true;
  std::vector<int> ch;
};

struct common_interval_tree {
  int rt = -1;
  std::vector<common_interval_node> tr;
};

namespace common_interval_internal {

struct maximum_monoid {
  using value_type = int;
  static value_type unit() { return std::numeric_limits<int>::lowest() / 4; }
  static value_type op(value_type a, value_type b) { return std::max(a, b); }
};

struct addition_action {
  using value_type = int;
  static value_type unit() { return 0; }
  static value_type composition(value_type nw, value_type old) {
    return nw + old;
  }
  static int apply(value_type add, int val) { return val + add; }
};

} // namespace common_interval_internal

/// @brief Build the strong-interval Hasse tree of a permutation.
/// For every possible left endpoint, a lazy segment tree maintains
/// `max-min-length+1` for the suffix ending at the current position. Monotone
/// min/max stacks update this value on exactly the ranges whose extrema
/// change, so value zero detects a new common interval. A second stack merges
/// adjacent value ranges into linear nodes; otherwise all nodes up to the
/// detected boundary form one prime node. Every merge is permanent, giving a
/// laminar tree containing exactly the strong intervals.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int sz = int(p.size());
  assert(sz > 0);
  std::vector<bool> vis(sz);
  for (int val : p) {
    assert(0 <= val && val < sz);
    assert(!vis[val]);
    vis[val] = true;
  }

  using segment_tree = lazy_segtree<common_interval_internal::maximum_monoid,
                                    common_interval_internal::addition_action>;
  segment_tree seg(sz);
  std::vector<int> smi;
  std::vector<int> smx;
  std::vector<int> st;
  common_interval_tree res;
  res.tr.reserve(sz * 2 - 1);

  auto ins = [&](common_interval_node u) {
    res.tr.push_back(std::move(u));
    return int(res.tr.size()) - 1;
  };
  auto adj = [&](int a, int b) {
    return res.tr[a].mn == res.tr[b].mx || res.tr[a].mx == res.tr[b].mn;
  };

  for (int idx = 0; idx < sz; idx++) {
    int r = idx;
    while (!smi.empty() && p[smi.back()] > p[idx]) {
      smi.pop_back();
      int l = smi.empty() ? 0 : smi.back() + 1;
      seg.apply(l, r, p[idx] - p[r - 1]);
      r = l;
    }
    smi.push_back(idx);

    r = idx;
    while (!smx.empty() && p[smx.back()] < p[idx]) {
      smx.pop_back();
      int l = smx.empty() ? 0 : smx.back() + 1;
      seg.apply(l, r, -p[idx] + p[r - 1]);
      r = l;
    }
    smx.push_back(idx);
    seg.apply(0, idx, 1);

    int cur =
        ins(common_interval_node{idx, idx + 1, p[idx], p[idx] + 1, true, {}});
    while (true) {
      if (!st.empty()) {
        int top = st.back();
        if (res.tr[top].lin && !res.tr[top].ch.empty() &&
            adj(cur, res.tr[top].ch.back())) {
          st.pop_back();
          res.tr[top].ch.push_back(cur);
          res.tr[top].l = std::min(res.tr[top].l, res.tr[cur].l);
          res.tr[top].r = std::max(res.tr[top].r, res.tr[cur].r);
          res.tr[top].mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          res.tr[top].mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          cur = top;
          continue;
        }
        if (adj(top, cur)) {
          st.pop_back();
          common_interval_node tmp;
          tmp.l = res.tr[top].l;
          tmp.r = res.tr[cur].r;
          tmp.mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          tmp.mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          tmp.lin = true;
          tmp.ch = {top, cur};
          cur = ins(std::move(tmp));
          continue;
        }
      }

      st.push_back(cur);
      if (st.size() == 1) {
        break;
      }
      int l = res.tr[st.back()].l;
      if (seg.prod(0, l) != 0) {
        break;
      }

      int top = st.back();
      st.pop_back();
      common_interval_node tm = res.tr[top];
      tm.lin = false;
      tm.ch = {top};
      cur = ins(std::move(tm));
      do {
        assert(!st.empty());
        top = st.back();
        st.pop_back();
        res.tr[cur].ch.push_back(top);
        res.tr[cur].l = std::min(res.tr[cur].l, res.tr[top].l);
        res.tr[cur].r = std::max(res.tr[cur].r, res.tr[top].r);
        res.tr[cur].mn = std::min(res.tr[cur].mn, res.tr[top].mn);
        res.tr[cur].mx = std::max(res.tr[cur].mx, res.tr[top].mx);
      } while (res.tr[cur].r - res.tr[cur].l !=
               res.tr[cur].mx - res.tr[cur].mn);
      std::reverse(res.tr[cur].ch.begin(), res.tr[cur].ch.end());
    }
    seg.set(idx, 0);
  }
  assert(st.size() == 1);
  res.rt = st.back();
  return res;
}

} // namespace noya

#endif // NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP
#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

/// @complexity Time: O(n log n).
/// Space: O(n).

/// @complexity Time: O(n) build and O(log n) point/range operation.
/// Space: O(n).

namespace noya {

/// @brief Lazy segment tree for a monoid acted on by a mapping monoid.
/// Monoid provides `value_type`, `unit()`, and `op(l, r)`.
/// Action provides `value_type`, `unit()`, `composition(newer, older)`, and
/// `apply(tag, monoid_value)`.
template <class Monoid, class Action> struct lazy_segtree {
  using S = typename Monoid::value_type;
  using F = typename Action::value_type;

  int n = 0;
  int sz = 1;
  int log = 0;
  std::vector<S> dat;
  std::vector<F> lz;

  lazy_segtree() : lazy_segtree(0) {}
  explicit lazy_segtree(int n_) { build(n_); }
  explicit lazy_segtree(const std::vector<S> &a) { build(a); }

  void build(int n_) { build(std::vector<S>(n_, Monoid::unit())); }

  void build(const std::vector<S> &a) {
    n = int(a.size());
    sz = 1;
    log = 0;
    while (sz < n) {
      sz <<= 1;
      log++;
    }
    dat.assign(sz << 1, Monoid::unit());
    lz.assign(sz, Action::unit());
    for (int i = 0; i < n; i++) {
      dat[sz + i] = a[i];
    }
    for (int u = sz - 1; u >= 1; u--) {
      pull(u);
    }
  }

  void set(int pos, const S &val) {
    assert(0 <= pos && pos < n);
    pos += sz;
    for (int h = log; h >= 1; h--) {
      push(pos >> h);
    }
    dat[pos] = val;
    for (int h = 1; h <= log; h++) {
      pull(pos >> h);
    }
  }

  S get(int pos) {
    assert(0 <= pos && pos < n);
    pos += sz;
    for (int h = log; h >= 1; h--) {
      push(pos >> h);
    }
    return dat[pos];
  }

  S prod(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return Monoid::unit();
    }
    l += sz;
    r += sz;
    for (int h = log; h >= 1; h--) {
      if (((l >> h) << h) != l) {
        push(l >> h);
      }
      if (((r >> h) << h) != r) {
        push((r - 1) >> h);
      }
    }
    S arr = Monoid::unit();
    S b = Monoid::unit();
    while (l < r) {
      if (l & 1) {
        arr = Monoid::op(arr, dat[l++]);
      }
      if (r & 1) {
        b = Monoid::op(dat[--r], b);
      }
      l >>= 1;
      r >>= 1;
    }
    return Monoid::op(arr, b);
  }

  S all_prod() const { return dat[1]; }

  /// @brief Apply an action to every element in [l, r).
  void apply(int l, int r, const F &tag) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    l += sz;
    r += sz;
    for (int h = log; h >= 1; h--) {
      if (((l >> h) << h) != l) {
        push(l >> h);
      }
      if (((r >> h) << h) != r) {
        push((r - 1) >> h);
      }
    }
    int l0 = l;
    int r0 = r;
    while (l < r) {
      if (l & 1) {
        all_apply(l++, tag);
      }
      if (r & 1) {
        all_apply(--r, tag);
      }
      l >>= 1;
      r >>= 1;
    }
    l = l0;
    r = r0;
    for (int h = 1; h <= log; h++) {
      if (((l >> h) << h) != l) {
        pull(l >> h);
      }
      if (((r >> h) << h) != r) {
        pull((r - 1) >> h);
      }
    }
  }

private:
  void pull(int u) {
    dat[u] = Monoid::op(dat[u << 1], dat[u << 1 | 1]);
  }

  void all_apply(int u, const F &tag) {
    dat[u] = Action::apply(tag, dat[u]);
    if (u < sz) {
      lz[u] = Action::composition(tag, lz[u]);
    }
  }

  void push(int u) {
    all_apply(u << 1, lz[u]);
    all_apply(u << 1 | 1, lz[u]);
    lz[u] = Action::unit();
  }
};

} // namespace noya

namespace noya {

struct common_interval_node {
  int l = 0;
  int r = 0;
  int mn = 0;
  int mx = 0;
  bool lin = true;
  std::vector<int> ch;
};

struct common_interval_tree {
  int rt = -1;
  std::vector<common_interval_node> tr;
};

namespace common_interval_internal {

struct maximum_monoid {
  using value_type = int;
  static value_type unit() { return std::numeric_limits<int>::lowest() / 4; }
  static value_type op(value_type a, value_type b) { return std::max(a, b); }
};

struct addition_action {
  using value_type = int;
  static value_type unit() { return 0; }
  static value_type composition(value_type nw, value_type old) {
    return nw + old;
  }
  static int apply(value_type add, int val) { return val + add; }
};

} // namespace common_interval_internal

/// @brief Build the strong-interval Hasse tree of a permutation.
/// For every possible left endpoint, a lazy segment tree maintains
/// `max-min-length+1` for the suffix ending at the current position. Monotone
/// min/max stacks update this value on exactly the ranges whose extrema
/// change, so value zero detects a new common interval. A second stack merges
/// adjacent value ranges into linear nodes; otherwise all nodes up to the
/// detected boundary form one prime node. Every merge is permanent, giving a
/// laminar tree containing exactly the strong intervals.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int sz = int(p.size());
  assert(sz > 0);
  std::vector<bool> vis(sz);
  for (int val : p) {
    assert(0 <= val && val < sz);
    assert(!vis[val]);
    vis[val] = true;
  }

  using segment_tree = lazy_segtree<common_interval_internal::maximum_monoid,
                                    common_interval_internal::addition_action>;
  segment_tree seg(sz);
  std::vector<int> smi;
  std::vector<int> smx;
  std::vector<int> st;
  common_interval_tree res;
  res.tr.reserve(sz * 2 - 1);

  auto ins = [&](common_interval_node u) {
    res.tr.push_back(std::move(u));
    return int(res.tr.size()) - 1;
  };
  auto adj = [&](int a, int b) {
    return res.tr[a].mn == res.tr[b].mx || res.tr[a].mx == res.tr[b].mn;
  };

  for (int idx = 0; idx < sz; idx++) {
    int r = idx;
    while (!smi.empty() && p[smi.back()] > p[idx]) {
      smi.pop_back();
      int l = smi.empty() ? 0 : smi.back() + 1;
      seg.apply(l, r, p[idx] - p[r - 1]);
      r = l;
    }
    smi.push_back(idx);

    r = idx;
    while (!smx.empty() && p[smx.back()] < p[idx]) {
      smx.pop_back();
      int l = smx.empty() ? 0 : smx.back() + 1;
      seg.apply(l, r, -p[idx] + p[r - 1]);
      r = l;
    }
    smx.push_back(idx);
    seg.apply(0, idx, 1);

    int cur =
        ins(common_interval_node{idx, idx + 1, p[idx], p[idx] + 1, true, {}});
    while (true) {
      if (!st.empty()) {
        int top = st.back();
        if (res.tr[top].lin && !res.tr[top].ch.empty() &&
            adj(cur, res.tr[top].ch.back())) {
          st.pop_back();
          res.tr[top].ch.push_back(cur);
          res.tr[top].l = std::min(res.tr[top].l, res.tr[cur].l);
          res.tr[top].r = std::max(res.tr[top].r, res.tr[cur].r);
          res.tr[top].mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          res.tr[top].mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          cur = top;
          continue;
        }
        if (adj(top, cur)) {
          st.pop_back();
          common_interval_node tmp;
          tmp.l = res.tr[top].l;
          tmp.r = res.tr[cur].r;
          tmp.mn = std::min(res.tr[top].mn, res.tr[cur].mn);
          tmp.mx = std::max(res.tr[top].mx, res.tr[cur].mx);
          tmp.lin = true;
          tmp.ch = {top, cur};
          cur = ins(std::move(tmp));
          continue;
        }
      }

      st.push_back(cur);
      if (st.size() == 1) {
        break;
      }
      int l = res.tr[st.back()].l;
      if (seg.prod(0, l) != 0) {
        break;
      }

      int top = st.back();
      st.pop_back();
      common_interval_node tm = res.tr[top];
      tm.lin = false;
      tm.ch = {top};
      cur = ins(std::move(tm));
      do {
        assert(!st.empty());
        top = st.back();
        st.pop_back();
        res.tr[cur].ch.push_back(top);
        res.tr[cur].l = std::min(res.tr[cur].l, res.tr[top].l);
        res.tr[cur].r = std::max(res.tr[cur].r, res.tr[top].r);
        res.tr[cur].mn = std::min(res.tr[cur].mn, res.tr[top].mn);
        res.tr[cur].mx = std::max(res.tr[cur].mx, res.tr[top].mx);
      } while (res.tr[cur].r - res.tr[cur].l !=
               res.tr[cur].mx - res.tr[cur].mn);
      std::reverse(res.tr[cur].ch.begin(), res.tr[cur].ch.end());
    }
    seg.set(idx, 0);
  }
  assert(st.size() == 1);
  res.rt = st.back();
  return res;
}

} // namespace noya