Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions other_algorithms/permutation_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

// Permutation tree
// Complexity: O(N log N)
// https://codeforces.com/blog/entry/78898 https://yukicoder.me/problems/no/1720
// https://codeforces.com/blog/entry/78898 https://judge.yosupo.jp/problem/common_interval_decomposition_tree
struct permutation_tree {
enum NodeType {
JoinAsc,
Expand All @@ -17,13 +17,14 @@ struct permutation_tree {
Leaf,
None,
};
struct node {
struct Node {
NodeType tp;
int L, R; // i in [L, R)
int mini, maxi; // A[i] in [mini, maxi]
int par_id = -1;
std::vector<int> child;
int sz() const { return R - L; }
template <class OStream> friend OStream &operator<<(OStream &os, const node &n) {
template <class OStream> friend OStream &operator<<(OStream &os, const Node &n) {
os << "[[" << n.L << ',' << n.R << ")(ch:";
for (auto i : n.child) os << i << ',';
return os << ")(tp=" << n.tp << ")]";
Expand All @@ -32,14 +33,14 @@ struct permutation_tree {

int root;
std::vector<int> A;
std::vector<node> nodes;
std::vector<Node> nodes;

void _add_child(int parid, int chid) {
nodes[parid].child.push_back(chid);
nodes[parid].L = std::min(nodes[parid].L, nodes[chid].L);
nodes[parid].R = std::max(nodes[parid].R, nodes[chid].R);
nodes[parid].mini = std::min(nodes[parid].mini, nodes[chid].mini);
nodes[parid].maxi = std::max(nodes[parid].maxi, nodes[chid].maxi);
void _add_child(int par_id, int chid) {
nodes[par_id].child.push_back(chid);
nodes[par_id].L = std::min(nodes[par_id].L, nodes[chid].L);
nodes[par_id].R = std::max(nodes[par_id].R, nodes[chid].R);
nodes[par_id].mini = std::min(nodes[par_id].mini, nodes[chid].mini);
nodes[par_id].maxi = std::max(nodes[par_id].maxi, nodes[chid].maxi);
}

permutation_tree() : root(-1) {}
Expand All @@ -62,15 +63,15 @@ struct permutation_tree {
lo.push_back(i);

int h = nodes.size();
nodes.push_back({NodeType::Leaf, i, i + 1, A[i], A[i], std::vector<int>{}});
nodes.push_back({NodeType::Leaf, i, i + 1, A[i], A[i], -1, std::vector<int>{}});

while (true) {
NodeType join_tp = NodeType::None;
if (!st.empty() and nodes[st.back()].maxi + 1 == nodes[h].mini) join_tp = JoinAsc;
if (!st.empty() and nodes[h].maxi + 1 == nodes[st.back()].mini) join_tp = JoinDesc;

if (!st.empty() and join_tp != NodeType::None) {
const node &vtp = nodes[st.back()];
const Node &vtp = nodes[st.back()];
// Insert v as the child of the top node in the stack
if (join_tp == vtp.tp) {
// Append child to existing Join node
Expand All @@ -81,15 +82,15 @@ struct permutation_tree {
// Make new join node (with exactly two children)
int j = st.back();
nodes.push_back(
{join_tp, nodes[j].L, nodes[j].R, nodes[j].mini, nodes[j].maxi, {j}});
{join_tp, nodes[j].L, nodes[j].R, nodes[j].mini, nodes[j].maxi, -1, {j}});
st.pop_back();
_add_child(nodes.size() - 1, h);
h = nodes.size() - 1;
}
} else if (seg.prod(0, i + 1 - nodes[h].sz()) == 0) {
// Make Cut node
int L = nodes[h].L, R = nodes[h].R, maxi = nodes[h].maxi, mini = nodes[h].mini;
nodes.push_back({NodeType::Cut, L, R, mini, maxi, {h}});
nodes.push_back({NodeType::Cut, L, R, mini, maxi, -1, {h}});
h = nodes.size() - 1;
do {
_add_child(h, st.back());
Expand All @@ -104,6 +105,11 @@ struct permutation_tree {
seg.add(0, i + 1, -1);
}
assert(st.size() == 1);

for (int i = 0; i < int(nodes.size()); i++) {
for (auto ch : nodes[i].child) nodes[ch].par_id = i;
}

root = st[0];
}

Expand Down
17 changes: 9 additions & 8 deletions other_algorithms/permutation_tree.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
---
title: Permutation tree (順列木)
title: Common interval decomposition tree / "Permutation tree" (順列木)
documentation_of: ./permutation_tree.hpp
---

与えられた $[0, \dots, N - 1]$ の置換 $\mathbf{A} = [A\_0, \dots, A\_{N - 1}]$ について,この連続部分列であってその長さがそれに含まれる要素の最大値と最小値の差に $1$ を加えた値と等しくなるようなものを全て列挙するのに役立つデータ構造
順列 $(0, \dots, N - 1)$ の置換 $P = (P\_0, \dots, P\_{N - 1})$ について,この連続部分列であってその区間をソートすると連番となるようなもの(たとえば、 $P = (1, 5, 3, 4, 7, 2, 6)$ における $(5, 3, 4)$ などの部分)を全列挙するのに役立つデータ構造

Permutation tree は区間のマージ過程を表す木として表現される.$N$ 個の葉は長さ $1$ の区間(単一の要素)に対応し,根は $\mathbf{A}$ 全体に対応する.
Permutation tree は区間のマージ過程を表す木として表現される.$N$ 個の葉は長さ $1$ の区間(単一の要素)に対応し,根は $P$ 全体に対応する.

葉以外の全ての頂点は `Join` と `Cut` いずれかの属性を持つ.`Join` 属性を持つ頂点は,その子を $c\_1, \dots, c\_k$ とおくと,任意の $1 \le i \le j \le k$ について頂点 $(c\_i, \dots, c\_j)$ が表す区間の和集合は上記の条件を満たす.また,全ての頂点について,その頂点が表す区間全体は上記の条件を満たす.そして,上記の条件を満たす区間はこれらに限られるというのが最も重要な性質である.


## 使用方法(例)

木の各頂点の情報はメンバ変数 `std::vector<node> nodes` に格納されている.特に根が格納されている位置を示す変数が `tree.root`.

```cpp
enum NodeType {
JoinAsc, // Join,特に A[i] の値が増加していく
JoinDesc, // Join,特に A[i] の値が減少していく
JoinAsc, // Join,特に P[i] の値が増加していく
JoinDesc, // Join,特に P[i] の値が減少していく
Cut, // Cut
Leaf, // 葉である
None,
};
struct node {
NodeType tp;
int L, R; // [L, R) : 頂点が表す区間
int mini, maxi; // 区間に含まれる A[i] (L <= i < R) の最小・最大値
int mini, maxi; // 区間に含まれる P[i] (L <= i < R) の最小・最大値
std::vector<int> child; // 子の頂点番号(昇順)
};
```
Expand All @@ -39,7 +38,7 @@ struct node {
上記の条件を満たす区間の個数だけを求めればよい問題.

```cpp
permutation_tree tree(A);
permutation_tree tree(P);

auto rec = [&](auto &&self, int now) -> long long {
long long ret = 1;
Expand Down Expand Up @@ -84,6 +83,8 @@ rec(rec, tree.root);
for (int i = 1; i <= K; i++) cout << dp[i][N].val() << '\n';
```

### [Common Interval Decomposition Tree](https://judge.yosupo.jp/problem/common_interval_decomposition_tree)

## リンク

1. [Tutorial on Permutation Tree (析合树) - Codeforces](https://codeforces.com/blog/entry/78898)
23 changes: 23 additions & 0 deletions other_algorithms/test/permutation_tree.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#define PROBLEM "https://judge.yosupo.jp/problem/common_interval_decomposition_tree"
#include "../permutation_tree.hpp"

#include <iostream>
using namespace std;

int main() {
cin.tie(nullptr), ios::sync_with_stdio(false);
int N;
cin >> N;

vector<int> P(N);
for (auto &p : P) cin >> p;

const permutation_tree pt(P);

cout << pt.nodes.size() << '\n';

for (const auto &node : pt.nodes) {
cout << node.par_id << ' ' << node.L << ' ' << node.R - 1 << ' '
<< (node.tp == permutation_tree::Cut ? "prime" : "linear") << '\n';
}
}