diff --git a/other_algorithms/permutation_tree.hpp b/other_algorithms/permutation_tree.hpp index c3ca345f..51315ea0 100644 --- a/other_algorithms/permutation_tree.hpp +++ b/other_algorithms/permutation_tree.hpp @@ -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, @@ -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 child; int sz() const { return R - L; } - template friend OStream &operator<<(OStream &os, const node &n) { + template 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 << ")]"; @@ -32,14 +33,14 @@ struct permutation_tree { int root; std::vector A; - std::vector nodes; + std::vector 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) {} @@ -62,7 +63,7 @@ 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{}}); + nodes.push_back({NodeType::Leaf, i, i + 1, A[i], A[i], -1, std::vector{}}); while (true) { NodeType join_tp = NodeType::None; @@ -70,7 +71,7 @@ struct permutation_tree { 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 @@ -81,7 +82,7 @@ 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; @@ -89,7 +90,7 @@ struct permutation_tree { } 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()); @@ -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]; } diff --git a/other_algorithms/permutation_tree.md b/other_algorithms/permutation_tree.md index 3597408f..88107a13 100644 --- a/other_algorithms/permutation_tree.md +++ b/other_algorithms/permutation_tree.md @@ -1,23 +1,22 @@ --- -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 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, @@ -25,7 +24,7 @@ enum NodeType { 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 child; // 子の頂点番号(昇順) }; ``` @@ -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; @@ -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) diff --git a/other_algorithms/test/permutation_tree.test.cpp b/other_algorithms/test/permutation_tree.test.cpp new file mode 100644 index 00000000..741715f5 --- /dev/null +++ b/other_algorithms/test/permutation_tree.test.cpp @@ -0,0 +1,23 @@ +#define PROBLEM "https://judge.yosupo.jp/problem/common_interval_decomposition_tree" +#include "../permutation_tree.hpp" + +#include +using namespace std; + +int main() { + cin.tie(nullptr), ios::sync_with_stdio(false); + int N; + cin >> N; + + vector 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'; + } +}