競技プログラミング用 C++ ライブラリ
tree/permutation_tree.hpp · GitHub で見る
共通ページ
#include "lib/tree/permutation_tree.hpp"
#include <iostream>
PermutationTree tree(std::vector<int>{1, 0, 3, 2});
std::cout << tree.count_intervals() << '\n'; // 7
for (int v : tree.children(tree.root())) {
std::cout << tree[v].l << ' ' << tree[v].r << '\n';
}
// 0 2
// 2 4
順列の共通区間分解木を作ります。入力は 0..n-1 を一度ずつ含む順列です。std::vector<int>・std::array<int,n> などの std::span<const int> に変換できる列を渡せます。入力を書き換えず、元の入力配列は構築後に破棄できます。長さは INT_MAX/2 以下としてください。
区間 [l,r) に現れる値が連続する、つまり max - min = r - l - 1 となる区間を共通区間と呼びます。木の頂点は、他の共通区間と部分的に重ならない共通区間(strong interval)に対応します。葉は各要素、根は順列全体です。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
PermutationTree() |
空の木を作る | \(O(1)\) |
PermutationTree(p) |
順列 p から構築 |
\(O(n+b\log b)\) |
build(p) |
順列から再構築。空列にも対応 | \(O(n+b\log b)\) |
size() |
葉と内部頂点を合わせた頂点数 | \(O(1)\) |
length() |
入力順列の長さ | \(O(1)\) |
empty() |
入力順列が空か | \(O(1)\) |
root() |
根の頂点 ID。空なら -1 | \(O(1)\) |
leaf(i) |
入力位置 i に対応する葉の ID。i と等しい |
\(O(1)\) |
operator[](v) |
頂点 v の Node への const 参照 |
\(O(1)\) |
nodes() |
全頂点の vector<Node> への const 参照 |
\(O(1)\) |
children(v) |
子の ID を位置区間の左端順に並べた span<const int>
|
\(O(1)\) |
order() |
根からの幅優先順の頂点 ID を返す span<const int>
|
\(O(1)\) |
count_intervals() |
長さ 1 を含む全共通区間の個数を int64_t で返す。空なら 0 |
\(O(1)\) |
Node の要素 |
内容 |
|---|---|
parent |
親の ID。根は -1 |
l / r
|
入力位置の半開区間 [l,r)
|
lo / hi
|
値の半開区間 [lo,hi)。常に r-l = hi-lo
|
type |
PermutationTree::Type による頂点の種類 |
Type の値 |
内容 |
|---|---|
Type::Leaf |
長さ 1 の葉 |
Type::Increasing |
子の値区間が左から増加する linear 頂点 |
Type::Decreasing |
子の値区間が左から減少する linear 頂点 |
Type::Prime |
それ以外の内部頂点 |
Increasing・Decreasing の頂点では、連続する任意の子の位置区間を合わせても共通区間になります。全共通区間は、木の頂点の区間と、このような子の連続区間の和集合で表せます。
頂点 ID は構築順なので、親子の ID の大小関係は保証しません。葉の ID は 0..n-1 です。order() では必ず親が子より先に現れるため、その逆順で木 DP を行えます。子は必ず左から右の順です。空でなければ頂点数は最大 2n-1 です。木が深い場合も、構築と走査順の生成に再帰を使いません。
コピー・ムーブに対応します。返された参照や span は再構築・代入・移動・破棄で無効になる場合があります。再構築では出力配列の容量を再利用します。
長さを \(n\)、内部 RMQ のブロック数を \(b=\lceil n/32\rceil\) とすると、構築時間・一時空間は \(O(n+b\log b)\) です。構築後の保持空間は \(O(n)\)、表の参照操作は全て \(O(1)\) です。空列の構築は \(O(1)\) です。
#ifndef PERMUTATION_TREE_HPP
#define PERMUTATION_TREE_HPP
#include "../components/static_range_min.hpp"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <limits>
#include <span>
#include <vector>
class PermutationTree {
public:
enum class Type : unsigned char { Leaf, Increasing, Decreasing, Prime };
struct Node {
int parent, l, r, lo, hi;
Type type;
};
private:
int root_ = -1;
std::int64_t count_ = 0;
std::vector<Node> nodes_;
std::vector<int> offsets_, children_, order_;
int add_node(int l, int r, int lo, int hi, Type type) {
int id = size();
nodes_.push_back({-1, l, r, lo, hi, type});
return id;
}
void pack(int n) {
int m = size();
offsets_.assign(m + 1, 0);
for (auto &node : nodes_) if (node.parent >= 0) ++offsets_[node.parent + 1];
for (int i = 0; i < m; ++i) offsets_[i + 1] += offsets_[i];
children_.resize(m - 1);
std::vector<int> cursor = offsets_;
for (int i = 0; i < n; ++i) {
int v = i;
while (nodes_[v].parent >= 0) {
int parent = nodes_[v].parent;
children_[cursor[parent]++] = v;
if (nodes_[parent].l != i) break;
v = parent;
}
}
count_ = n;
for (int i = n; i < m; ++i) {
std::int64_t k = offsets_[i + 1] - offsets_[i];
count_ += nodes_[i].type == Type::Prime ? 1 : k * (k - 1) / 2;
}
order_.clear();
order_.reserve(m);
order_.push_back(root_);
for (int i = 0; i < m; ++i)
for (int child : children(order_[i])) order_.push_back(child);
}
public:
PermutationTree() = default;
explicit PermutationTree(std::span<const int> permutation) { build(permutation); }
void build(std::span<const int> permutation) {
assert(permutation.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
int n = static_cast<int>(permutation.size());
root_ = -1;
count_ = 0;
nodes_.clear();
offsets_.clear();
children_.clear();
order_.clear();
if (!n) return;
nodes_.reserve(2 * std::size_t(n) - 1);
std::vector<int> inverse(n, -1);
for (int i = 0; i < n; ++i) {
int x = permutation[i];
assert(0 <= x && x < n && inverse[x] < 0);
inverse[x] = i;
add_node(i, i + 1, x, x + 1, Type::Leaf);
}
struct Frame { int l, lo, hi; };
struct Interval { int l, r, lo, hi; };
std::vector<Interval> intervals;
{
StaticRangeMin minimum(std::move(inverse));
std::vector<Frame> stack;
stack.reserve(n);
intervals.reserve(n - 1);
for (int r = 1; r <= n; ++r) {
int x = permutation[r - 1];
Frame current{r - 1, x, x + 1};
while (!stack.empty()) {
auto &top = stack.back();
top.lo = std::min(top.lo, current.lo);
top.hi = std::max(top.hi, current.hi);
if (top.hi - top.lo == r - top.l) {
current = top;
intervals.push_back({top.l, r, top.lo, top.hi});
stack.pop_back();
} else if (minimum.prod(top.lo, top.hi) < top.l) {
Frame removed = top;
stack.pop_back();
assert(!stack.empty());
stack.back().lo = std::min(stack.back().lo, removed.lo);
stack.back().hi = std::max(stack.back().hi, removed.hi);
} else break;
}
stack.push_back(current);
}
assert(stack.size() == 1 && stack[0].l == 0);
}
std::vector<int> at(n), next(n);
for (int i = 0; i < n; ++i) at[i] = i, next[i] = i + 1;
for (auto interval : intervals) {
int l = interval.l, r = interval.r, middle = next[l];
int first = at[l], id;
if (next[middle] == r) {
int second = at[middle];
Type type = nodes_[first].hi == nodes_[second].lo ? Type::Increasing : Type::Decreasing;
if (nodes_[first].type == type) {
id = first;
nodes_[id].r = r;
nodes_[id].lo = interval.lo;
nodes_[id].hi = interval.hi;
nodes_[second].parent = id;
} else {
id = add_node(l, r, interval.lo, interval.hi, type);
nodes_[first].parent = nodes_[second].parent = id;
}
} else {
id = add_node(l, r, interval.lo, interval.hi, Type::Prime);
for (int i = l; i < r; i = next[i]) nodes_[at[i]].parent = id;
}
at[l] = id;
next[l] = r;
}
root_ = at[0];
assert(next[0] == n);
pack(n);
}
int size() const { return static_cast<int>(nodes_.size()); }
bool empty() const { return nodes_.empty(); }
int root() const { return empty() ? -1 : root_; }
int length() const { return empty() ? 0 : nodes_[root_].r; }
int leaf(int i) const { assert(0 <= i && i < length()); return i; }
const Node &operator[](int i) const {
assert(0 <= i && i < size());
return nodes_[i];
}
const std::vector<Node> &nodes() const { return nodes_; }
std::span<const int> children(int v) const {
assert(0 <= v && v < size());
return std::span<const int>(children_).subspan(offsets_[v], offsets_[v + 1] - offsets_[v]);
}
std::span<const int> order() const { return order_; }
std::int64_t count_intervals() const { return empty() ? 0 : count_; }
};
#endif
#line 1 "tree/permutation_tree.hpp"
#line 1 "components/static_range_min.hpp"
#line 1 "components/default_init_allocator.hpp"
#include <memory>
#include <new>
template <class T>
struct DefaultInitAllocator : std::allocator<T> {
template <class U>
struct rebind {
using other = DefaultInitAllocator<U>;
};
DefaultInitAllocator() = default;
template <class U>
DefaultInitAllocator(const DefaultInitAllocator<U> &) noexcept {}
template <class U>
void construct(U *p) { ::new (static_cast<void *>(p)) U; }
};
#line 1 "data_structure/sparse_table.hpp"
#line 5 "data_structure/sparse_table.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
template <class S, auto op, auto e = nullptr>
class SparseTable {
using Value = std::conditional_t<std::is_same_v<S, bool>, std::uint8_t, S>;
using Reference = std::conditional_t<std::is_same_v<S, bool>, bool, const S &>;
int n = 0;
std::vector<std::vector<Value, DefaultInitAllocator<Value>>> data;
static decltype(auto) combine(const Value &a, const Value &b) {
if constexpr (std::is_same_v<S, bool> || !std::is_invocable_v<decltype(op), const S &, const S &>) return S(op(S(a), S(b)));
else return op(a, b);
}
public:
SparseTable() = default;
SparseTable(const SparseTable &) = default;
SparseTable &operator=(const SparseTable &) = default;
SparseTable(SparseTable &&other) noexcept : n(std::exchange(other.n, 0)), data(std::move(other.data)) {}
SparseTable &operator=(SparseTable &&other) noexcept {
if (this != &other) {
n = std::exchange(other.n, 0);
data = std::move(other.data);
}
return *this;
}
explicit SparseTable(const std::vector<S> &a) { build(a); }
void clear() {
data.clear();
n = 0;
}
void build(const std::vector<S> &a) {
assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
build(static_cast<int>(a.size()), [&](int i) -> S { return a[i]; });
}
template <class F>
void build(int size, F value) {
assert(size >= 0);
n = size;
data.resize(std::bit_width(unsigned(n)));
if (!n) return;
if constexpr (std::is_default_constructible_v<Value>) {
data[0].resize(n);
for (int i = 0; i < n; ++i) data[0][i] = value(i);
} else {
data[0].clear();
data[0].reserve(n);
for (int i = 0; i < n; ++i) data[0].push_back(value(i));
}
for (int k = 1; k < std::bit_width(unsigned(n)); ++k) {
int length = n - (1U << k) + 1, half = 1U << (k - 1);
if constexpr (std::is_default_constructible_v<Value>) {
data[k].resize(length);
for (int i = 0; i < length; ++i) data[k][i] = combine(data[k - 1][i], data[k - 1][i + half]);
} else {
data[k].clear();
data[k].reserve(length);
for (int i = 0; i < length; ++i) data[k].push_back(combine(data[k - 1][i], data[k - 1][i + half]));
}
}
}
int size() const { return n; }
bool empty() const { return n == 0; }
Reference operator[](int i) const {
assert(0 <= i && i < n);
return data[0][i];
}
[[gnu::always_inline]] S prod(int l, int r) const {
assert(0 <= l && l <= r && r <= n);
if constexpr (std::is_same_v<decltype(e), std::nullptr_t>) assert(l < r);
else if (l == r) return e();
return prod_unchecked(l, r);
}
[[gnu::always_inline]] S prod_unchecked(int l, int r) const {
int k = std::bit_width(unsigned(r - l)) - 1;
return combine(data[k][l], data[k][r - (1U << k)]);
}
S all_prod() const { return prod(0, n); }
};
#line 7 "components/static_range_min.hpp"
#include <array>
#line 12 "components/static_range_min.hpp"
#include <span>
#line 16 "components/static_range_min.hpp"
template <class T = int, bool FastBoundaries = false> class StaticRangeMin {
static T minimum(T a, T b) { return b < a ? b : a; }
std::vector<T> values_;
std::vector<std::uint32_t, DefaultInitAllocator<std::uint32_t>> masks_;
SparseTable<T, minimum> blocks_;
struct Boundaries { std::vector<T> prefix, suffix; };
struct Empty {};
[[no_unique_address]] std::conditional_t<FastBoundaries, Boundaries, Empty> boundaries_;
T within(int l, int r) const {
auto mask = masks_[r - 1] & (~std::uint32_t(0) << (l & 31));
return values_[(l & ~31) + std::countr_zero(mask)];
}
public:
StaticRangeMin() = default;
explicit StaticRangeMin(std::vector<T> values) { build(std::move(values)); }
void build(std::vector<T> values) {
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()));
values_ = std::move(values);
int n = size();
masks_.resize(n);
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
std::uint32_t mask = 0;
for (int i = first; i < last; ++i) {
while (mask && !(values_[first + std::bit_width(mask) - 1] < values_[i]))
mask &= ~(std::uint32_t(1) << (std::bit_width(mask) - 1));
mask |= std::uint32_t(1) << (i - first);
masks_[i] = mask;
}
first = last;
}
if constexpr (FastBoundaries) {
boundaries_.prefix = values_, boundaries_.suffix = values_;
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
for (int i = first + 1; i < last; ++i) boundaries_.prefix[i] = minimum(boundaries_.prefix[i - 1], values_[i]);
for (int i = last - 1; i > first; --i) boundaries_.suffix[i - 1] = minimum(values_[i - 1], boundaries_.suffix[i]);
first = last;
}
}
blocks_.build(n ? (n - 1) / 32 + 1 : 0, [&](int i) {
int last = i * 32 + std::min(32, n - i * 32);
if constexpr (FastBoundaries) return T(boundaries_.prefix[last - 1]);
else return within(i * 32, last);
});
}
int size() const { return static_cast<int>(values_.size()); }
bool empty() const { return values_.empty(); }
std::conditional_t<std::is_same_v<T, bool>, bool, const T &> operator[](int i) const {
assert(0 <= i && i < size());
return values_[i];
}
T prod(int l, int r) const {
assert(0 <= l && l < r && r <= size());
int a = l / 32, b = (r - 1) / 32;
if (a == b) return within(l, r);
T value = [&] {
if constexpr (FastBoundaries) return minimum(boundaries_.suffix[l], boundaries_.prefix[r - 1]);
else return minimum(within(l, (a + 1) * 32), within(b * 32, r));
}();
if (a + 1 < b) value = minimum(value, blocks_.prod_unchecked(a + 1, b));
return value;
}
std::vector<T> prod_many(std::span<const std::pair<int, int>> ranges) const {
std::vector<T> result;
result.reserve(ranges.size());
if constexpr (FastBoundaries) {
for (auto [l, r] : ranges) result.emplace_back(prod(l, r));
} else {
std::array<int, 32> left, right;
for (std::size_t first = 0; first < ranges.size(); first += 32) {
int count = int(std::min<std::size_t>(32, ranges.size() - first));
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
assert(0 <= l && l < r && r <= size());
int a = l & ~31, b = (r - 1) & ~31;
auto mask = masks_[a == b ? r - 1 : a + 31] & (~std::uint32_t(0) << (l & 31));
left[i] = a + std::countr_zero(mask);
right[i] = a == b ? left[i] : b + std::countr_zero(masks_[r - 1]);
#if defined(__GNUC__) || defined(__clang__)
if constexpr (!std::is_same_v<T, bool>) {
__builtin_prefetch(values_.data() + left[i]);
__builtin_prefetch(values_.data() + right[i]);
}
#endif
}
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
T value = minimum(values_[left[i]], values_[right[i]]);
int a = l / 32 + 1, b = (r - 1) / 32;
if (a < b) value = minimum(value, blocks_.prod_unchecked(a, b));
result.emplace_back(std::move(value));
}
}
}
return result;
}
T all_prod() const { return prod(0, size()); }
};
#line 11 "tree/permutation_tree.hpp"
class PermutationTree {
public:
enum class Type : unsigned char { Leaf, Increasing, Decreasing, Prime };
struct Node {
int parent, l, r, lo, hi;
Type type;
};
private:
int root_ = -1;
std::int64_t count_ = 0;
std::vector<Node> nodes_;
std::vector<int> offsets_, children_, order_;
int add_node(int l, int r, int lo, int hi, Type type) {
int id = size();
nodes_.push_back({-1, l, r, lo, hi, type});
return id;
}
void pack(int n) {
int m = size();
offsets_.assign(m + 1, 0);
for (auto &node : nodes_) if (node.parent >= 0) ++offsets_[node.parent + 1];
for (int i = 0; i < m; ++i) offsets_[i + 1] += offsets_[i];
children_.resize(m - 1);
std::vector<int> cursor = offsets_;
for (int i = 0; i < n; ++i) {
int v = i;
while (nodes_[v].parent >= 0) {
int parent = nodes_[v].parent;
children_[cursor[parent]++] = v;
if (nodes_[parent].l != i) break;
v = parent;
}
}
count_ = n;
for (int i = n; i < m; ++i) {
std::int64_t k = offsets_[i + 1] - offsets_[i];
count_ += nodes_[i].type == Type::Prime ? 1 : k * (k - 1) / 2;
}
order_.clear();
order_.reserve(m);
order_.push_back(root_);
for (int i = 0; i < m; ++i)
for (int child : children(order_[i])) order_.push_back(child);
}
public:
PermutationTree() = default;
explicit PermutationTree(std::span<const int> permutation) { build(permutation); }
void build(std::span<const int> permutation) {
assert(permutation.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
int n = static_cast<int>(permutation.size());
root_ = -1;
count_ = 0;
nodes_.clear();
offsets_.clear();
children_.clear();
order_.clear();
if (!n) return;
nodes_.reserve(2 * std::size_t(n) - 1);
std::vector<int> inverse(n, -1);
for (int i = 0; i < n; ++i) {
int x = permutation[i];
assert(0 <= x && x < n && inverse[x] < 0);
inverse[x] = i;
add_node(i, i + 1, x, x + 1, Type::Leaf);
}
struct Frame { int l, lo, hi; };
struct Interval { int l, r, lo, hi; };
std::vector<Interval> intervals;
{
StaticRangeMin minimum(std::move(inverse));
std::vector<Frame> stack;
stack.reserve(n);
intervals.reserve(n - 1);
for (int r = 1; r <= n; ++r) {
int x = permutation[r - 1];
Frame current{r - 1, x, x + 1};
while (!stack.empty()) {
auto &top = stack.back();
top.lo = std::min(top.lo, current.lo);
top.hi = std::max(top.hi, current.hi);
if (top.hi - top.lo == r - top.l) {
current = top;
intervals.push_back({top.l, r, top.lo, top.hi});
stack.pop_back();
} else if (minimum.prod(top.lo, top.hi) < top.l) {
Frame removed = top;
stack.pop_back();
assert(!stack.empty());
stack.back().lo = std::min(stack.back().lo, removed.lo);
stack.back().hi = std::max(stack.back().hi, removed.hi);
} else break;
}
stack.push_back(current);
}
assert(stack.size() == 1 && stack[0].l == 0);
}
std::vector<int> at(n), next(n);
for (int i = 0; i < n; ++i) at[i] = i, next[i] = i + 1;
for (auto interval : intervals) {
int l = interval.l, r = interval.r, middle = next[l];
int first = at[l], id;
if (next[middle] == r) {
int second = at[middle];
Type type = nodes_[first].hi == nodes_[second].lo ? Type::Increasing : Type::Decreasing;
if (nodes_[first].type == type) {
id = first;
nodes_[id].r = r;
nodes_[id].lo = interval.lo;
nodes_[id].hi = interval.hi;
nodes_[second].parent = id;
} else {
id = add_node(l, r, interval.lo, interval.hi, type);
nodes_[first].parent = nodes_[second].parent = id;
}
} else {
id = add_node(l, r, interval.lo, interval.hi, Type::Prime);
for (int i = l; i < r; i = next[i]) nodes_[at[i]].parent = id;
}
at[l] = id;
next[l] = r;
}
root_ = at[0];
assert(next[0] == n);
pack(n);
}
int size() const { return static_cast<int>(nodes_.size()); }
bool empty() const { return nodes_.empty(); }
int root() const { return empty() ? -1 : root_; }
int length() const { return empty() ? 0 : nodes_[root_].r; }
int leaf(int i) const { assert(0 <= i && i < length()); return i; }
const Node &operator[](int i) const {
assert(0 <= i && i < size());
return nodes_[i];
}
const std::vector<Node> &nodes() const { return nodes_; }
std::span<const int> children(int v) const {
assert(0 <= v && v < size());
return std::span<const int>(children_).subspan(offsets_[v], offsets_[v + 1] - offsets_[v]);
}
std::span<const int> order() const { return order_; }
std::int64_t count_intervals() const { return empty() ? 0 : count_; }
};