Skip to content

common_interval_decomposition_tree.hpp

SECTIONData Structure INCLUDEnoya/ds/common_interval_decomposition_tree.hpp

给定 0..N-1 的排列,以候选栈和并查集在 O(N α(N)) 时间内构造所有强公共区间的析合树;位置区间和值域均为半开区间,孩子按位置排列。

Complexity: Time: \(O(n \alpha (n))\) amortized. Space: \(O(n)\).

AC 记录:common_interval_decomposition_tree。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n alpha(n)) amortized.
/// Space: O(n).

#include "atcoder/dsu.hpp"

#include <algorithm>
#include <cassert>
#include <utility>
#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;
};

/// @brief Build the strong-interval Hasse tree of a permutation.
/// Active adjacent values are joined in a DSU. Candidate left boundaries cache
/// the extrema of their examined suffix. Different components stop the scan:
/// an unseen value separates the suffix values. Once the whole suffix lies in
/// one component, a missing value must occur before its left boundary, so a
/// failed candidate can be discarded permanently. Each candidate is pushed
/// and popped at most once; forest merges build linear or prime nodes.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int n = int(p.size());
  assert(n > 0);
  struct checkpoint {
    int l, mn, mx;
  };
  std::vector<checkpoint> candidates;
  std::vector<int> forest;
  std::vector<bool> active(n);
  atcoder::dsu components(n);
  common_interval_tree res;
  res.tr.reserve(2 * n - 1);
  auto ins = [&](common_interval_node node) {
    res.tr.push_back(std::move(node));
    return int(res.tr.size()) - 1;
  };

  for (int i = 0; i < n; i++) {
    int value = p[i];
    assert(0 <= value && value < n && !active[value]);
    active[value] = true;
    if (value > 0 && active[value - 1])
      components.merge(value, value - 1);
    if (value + 1 < n && active[value + 1])
      components.merge(value, value + 1);

    int cur = ins({i, i + 1, value, value + 1, true, {}});
    int mn = value, mx = value + 1;
    while (!forest.empty()) {
      int left = -1;
      while (!candidates.empty()) {
        auto &c = candidates.back();
        c.mn = mn = std::min(c.mn, mn);
        c.mx = mx = std::max(c.mx, mx);
        if (!components.same(p[c.l], value))
          break;
        if (i + 1 - c.l == mx - mn)
          left = c.l;
        candidates.pop_back();
        if (left != -1)
          break;
      }
      if (left == -1)
        break;

      int top = forest.back();
      auto &a = res.tr[top];
      const auto &b = res.tr[cur];
      bool extend =
          a.lin && !a.ch.empty() &&
          ((res.tr[a.ch.front()].mn < res.tr[a.ch.back()].mn && a.mx == b.mn) ||
           (res.tr[a.ch.front()].mn > res.tr[a.ch.back()].mn && b.mx == a.mn));
      if (extend) {
        a.ch.push_back(cur);
        a.r = i + 1;
        a.mn = mn;
        a.mx = mx;
        cur = top;
        forest.pop_back();
      } else {
        common_interval_node node{left, i + 1, mn, mx, left == a.l, {cur}};
        while (!forest.empty() && res.tr[forest.back()].l >= left) {
          node.ch.push_back(forest.back());
          forest.pop_back();
        }
        std::reverse(node.ch.begin(), node.ch.end());
        cur = ins(std::move(node));
      }
    }
    const auto &node = res.tr[cur];
    candidates.push_back({node.l, node.mn, node.mx});
    forest.push_back(cur);
  }
  assert(forest.size() == 1);
  res.rt = forest.back();
  return res;
}

} // namespace noya

 // NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP
#ifndef NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP
#define NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP 1

/// @complexity Time: O(n alpha(n)) amortized.
/// Space: O(n).

#include "atcoder/dsu.hpp"

#include <algorithm>
#include <cassert>
#include <utility>
#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;
};

/// @brief Build the strong-interval Hasse tree of a permutation.
/// Active adjacent values are joined in a DSU. Candidate left boundaries cache
/// the extrema of their examined suffix. Different components stop the scan:
/// an unseen value separates the suffix values. Once the whole suffix lies in
/// one component, a missing value must occur before its left boundary, so a
/// failed candidate can be discarded permanently. Each candidate is pushed
/// and popped at most once; forest merges build linear or prime nodes.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int n = int(p.size());
  assert(n > 0);
  struct checkpoint {
    int l, mn, mx;
  };
  std::vector<checkpoint> candidates;
  std::vector<int> forest;
  std::vector<bool> active(n);
  atcoder::dsu components(n);
  common_interval_tree res;
  res.tr.reserve(2 * n - 1);
  auto ins = [&](common_interval_node node) {
    res.tr.push_back(std::move(node));
    return int(res.tr.size()) - 1;
  };

  for (int i = 0; i < n; i++) {
    int value = p[i];
    assert(0 <= value && value < n && !active[value]);
    active[value] = true;
    if (value > 0 && active[value - 1])
      components.merge(value, value - 1);
    if (value + 1 < n && active[value + 1])
      components.merge(value, value + 1);

    int cur = ins({i, i + 1, value, value + 1, true, {}});
    int mn = value, mx = value + 1;
    while (!forest.empty()) {
      int left = -1;
      while (!candidates.empty()) {
        auto &c = candidates.back();
        c.mn = mn = std::min(c.mn, mn);
        c.mx = mx = std::max(c.mx, mx);
        if (!components.same(p[c.l], value))
          break;
        if (i + 1 - c.l == mx - mn)
          left = c.l;
        candidates.pop_back();
        if (left != -1)
          break;
      }
      if (left == -1)
        break;

      int top = forest.back();
      auto &a = res.tr[top];
      const auto &b = res.tr[cur];
      bool extend =
          a.lin && !a.ch.empty() &&
          ((res.tr[a.ch.front()].mn < res.tr[a.ch.back()].mn && a.mx == b.mn) ||
           (res.tr[a.ch.front()].mn > res.tr[a.ch.back()].mn && b.mx == a.mn));
      if (extend) {
        a.ch.push_back(cur);
        a.r = i + 1;
        a.mn = mn;
        a.mx = mx;
        cur = top;
        forest.pop_back();
      } else {
        common_interval_node node{left, i + 1, mn, mx, left == a.l, {cur}};
        while (!forest.empty() && res.tr[forest.back()].l >= left) {
          node.ch.push_back(forest.back());
          forest.pop_back();
        }
        std::reverse(node.ch.begin(), node.ch.end());
        cur = ins(std::move(node));
      }
    }
    const auto &node = res.tr[cur];
    candidates.push_back({node.l, node.mn, node.mx});
    forest.push_back(cur);
  }
  assert(forest.size() == 1);
  res.rt = forest.back();
  return res;
}

} // namespace noya

#endif // NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP
/// @complexity Time: O(n alpha(n)) amortized.
/// Space: O(n).

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

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        return _leader(a);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;

    int _leader(int a) {
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = _leader(parent_or_size[a]);
    }
};

}  // namespace atcoder

#include <algorithm>
#include <cassert>
#include <utility>
#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;
};

/// @brief Build the strong-interval Hasse tree of a permutation.
/// Active adjacent values are joined in a DSU. Candidate left boundaries cache
/// the extrema of their examined suffix. Different components stop the scan:
/// an unseen value separates the suffix values. Once the whole suffix lies in
/// one component, a missing value must occur before its left boundary, so a
/// failed candidate can be discarded permanently. Each candidate is pushed
/// and popped at most once; forest merges build linear or prime nodes.
inline common_interval_tree
common_interval_decomposition_tree(const std::vector<int> &p) {
  int n = int(p.size());
  assert(n > 0);
  struct checkpoint {
    int l, mn, mx;
  };
  std::vector<checkpoint> candidates;
  std::vector<int> forest;
  std::vector<bool> active(n);
  atcoder::dsu components(n);
  common_interval_tree res;
  res.tr.reserve(2 * n - 1);
  auto ins = [&](common_interval_node node) {
    res.tr.push_back(std::move(node));
    return int(res.tr.size()) - 1;
  };

  for (int i = 0; i < n; i++) {
    int value = p[i];
    assert(0 <= value && value < n && !active[value]);
    active[value] = true;
    if (value > 0 && active[value - 1])
      components.merge(value, value - 1);
    if (value + 1 < n && active[value + 1])
      components.merge(value, value + 1);

    int cur = ins({i, i + 1, value, value + 1, true, {}});
    int mn = value, mx = value + 1;
    while (!forest.empty()) {
      int left = -1;
      while (!candidates.empty()) {
        auto &c = candidates.back();
        c.mn = mn = std::min(c.mn, mn);
        c.mx = mx = std::max(c.mx, mx);
        if (!components.same(p[c.l], value))
          break;
        if (i + 1 - c.l == mx - mn)
          left = c.l;
        candidates.pop_back();
        if (left != -1)
          break;
      }
      if (left == -1)
        break;

      int top = forest.back();
      auto &a = res.tr[top];
      const auto &b = res.tr[cur];
      bool extend =
          a.lin && !a.ch.empty() &&
          ((res.tr[a.ch.front()].mn < res.tr[a.ch.back()].mn && a.mx == b.mn) ||
           (res.tr[a.ch.front()].mn > res.tr[a.ch.back()].mn && b.mx == a.mn));
      if (extend) {
        a.ch.push_back(cur);
        a.r = i + 1;
        a.mn = mn;
        a.mx = mx;
        cur = top;
        forest.pop_back();
      } else {
        common_interval_node node{left, i + 1, mn, mx, left == a.l, {cur}};
        while (!forest.empty() && res.tr[forest.back()].l >= left) {
          node.ch.push_back(forest.back());
          forest.pop_back();
        }
        std::reverse(node.ch.begin(), node.ch.end());
        cur = ins(std::move(node));
      }
    }
    const auto &node = res.tr[cur];
    candidates.push_back({node.l, node.mn, node.mx});
    forest.push_back(cur);
  }
  assert(forest.size() == 1);
  res.rt = forest.back();
  return res;
}

} // namespace noya

 // NOYA_COMMON_INTERVAL_DECOMPOSITION_TREE_HPP