Skip to content

Commit 8d90724

Browse files
authored
Merge pull request #337 from hitonanode/static-top-tree
Add static top tree
2 parents c751c5b + da655a9 commit 8d90724

File tree

3 files changed

+390
-0
lines changed

3 files changed

+390
-0
lines changed

data_structure/static_toptree.hpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <utility>
4+
#include <vector>
5+
6+
// Structure of static top tree
7+
// https://atcoder.jp/contests/abc351/submissions/52777033
8+
struct static_toptree_structure {
9+
enum NodeType {
10+
Vertex,
11+
Compress,
12+
Rake,
13+
AddEdge,
14+
AddVertex,
15+
};
16+
17+
const std::vector<std::vector<int>> &to;
18+
std::vector<int> par;
19+
std::vector<int> heavy_child; // heavy_child[i] = child of i on heavy path
20+
21+
// toptree data
22+
int stt_root = -1;
23+
std::vector<int> P, L, R;
24+
std::vector<NodeType> T;
25+
26+
private:
27+
int hld_dfs(int now, int prv) {
28+
int sz = 1, max_sz = 0;
29+
for (int nxt : to.at(now)) {
30+
if (nxt == prv) continue;
31+
par.at(nxt) = now;
32+
int sub_sz = hld_dfs(nxt, now);
33+
sz += sub_sz;
34+
if (max_sz < sub_sz) max_sz = sub_sz, heavy_child.at(now) = nxt;
35+
}
36+
return sz;
37+
}
38+
39+
int create(int k, int l, int r, NodeType t) {
40+
if (k == -1) {
41+
k = P.size();
42+
P.push_back(-1), L.push_back(l), R.push_back(r), T.push_back(t);
43+
} else {
44+
P.at(k) = -1, L.at(k) = l, R.at(k) = r, T.at(k) = t;
45+
}
46+
47+
if (l != -1) P.at(l) = k;
48+
if (r != -1) P.at(r) = k;
49+
50+
return k;
51+
}
52+
53+
std::pair<int, int> merge(const std::vector<std::pair<int, int>> &a, NodeType t) {
54+
if (a.size() == 1) return a.front();
55+
56+
int u = 0;
57+
for (auto &[idx, sz] : a) u += sz;
58+
59+
std::vector<std::pair<int, int>> left, right;
60+
for (const auto &[idx, sz] : a) {
61+
(u > sz ? left : right).emplace_back(idx, sz), u -= sz * 2;
62+
}
63+
64+
auto [left_idx, left_sz] = merge(left, t);
65+
auto [right_idx, right_sz] = merge(right, t);
66+
67+
return {create(-1, left_idx, right_idx, t), left_sz + right_sz};
68+
}
69+
70+
std::pair<int, int> compress(int i) {
71+
std::vector<std::pair<int, int>> chs{add_vertex(i)};
72+
while (heavy_child.at(i) != -1) {
73+
i = heavy_child.at(i);
74+
chs.push_back(add_vertex(i));
75+
}
76+
77+
return merge(chs, Compress);
78+
}
79+
80+
std::pair<int, int> rake(int i) {
81+
std::vector<std::pair<int, int>> chs;
82+
for (int j : to.at(i)) {
83+
if (j == par.at(i) or j == heavy_child.at(i)) continue;
84+
chs.push_back(add_edge(j));
85+
}
86+
87+
return chs.empty() ? std::make_pair(-1, 0) : merge(chs, Rake);
88+
}
89+
90+
std::pair<int, int> add_edge(int i) {
91+
auto [c, sz] = compress(i);
92+
return {create(-1, c, -1, AddEdge), sz};
93+
}
94+
95+
std::pair<int, int> add_vertex(int i) {
96+
auto [c, sz] = rake(i);
97+
return {create(i, c, -1, c == -1 ? Vertex : AddVertex), sz + 1};
98+
}
99+
100+
public:
101+
static_toptree_structure(const std::vector<std::vector<int>> &to, int root) : to(to) {
102+
103+
const int n = to.size();
104+
105+
par.assign(n, -1), heavy_child.assign(n, -1);
106+
107+
hld_dfs(root, -1);
108+
109+
P.assign(n, -1), L.assign(n, -1), R.assign(n, -1), T.assign(n, Vertex);
110+
111+
stt_root = compress(root).first;
112+
}
113+
114+
int size() const { return P.size(); }
115+
116+
// Top tree の帰りがけ順に f() を呼ぶ
117+
// データの初期化などに利用可能
118+
template <class Callback> void dfs_postorder(Callback f) const {
119+
auto rec = [&](auto &&self, int now) -> void {
120+
if (L.at(now) != -1) self(self, L.at(now));
121+
if (R.at(now) != -1) self(self, R.at(now));
122+
f(now);
123+
};
124+
rec(rec, stt_root);
125+
}
126+
127+
// Top tree の v から根(!= もとの木の根)までのパス上で f() を呼ぶ
128+
// 一点更新などに利用可能
129+
template <class Callback> void path_to_root(int v, Callback f) const {
130+
while (v != -1) f(v), v = P.at(v);
131+
}
132+
};
133+
134+
// Static top tree
135+
template <class TreeDP> struct static_toptree {
136+
137+
using Point = typename TreeDP::Point;
138+
using Path = typename TreeDP::Path;
139+
140+
const static_toptree_structure &stts;
141+
TreeDP &tree_dp;
142+
143+
std::vector<Point> point;
144+
std::vector<Path> path;
145+
146+
private:
147+
void _update(int i) {
148+
if (stts.T.at(i) == static_toptree_structure::Vertex) {
149+
path.at(i) = tree_dp.vertex(i);
150+
} else if (stts.T.at(i) == static_toptree_structure::Compress) {
151+
path.at(i) = tree_dp.compress(path.at(stts.L.at(i)), path.at(stts.R.at(i)));
152+
} else if (stts.T.at(i) == static_toptree_structure::Rake) {
153+
point.at(i) = tree_dp.rake(point.at(stts.L.at(i)), point.at(stts.R.at(i)));
154+
} else if (stts.T.at(i) == static_toptree_structure::AddEdge) {
155+
point.at(i) = tree_dp.add_edge(path.at(stts.L.at(i)));
156+
} else if (stts.T.at(i) == static_toptree_structure::AddVertex) {
157+
path.at(i) = tree_dp.add_vertex(point.at(stts.L.at(i)), i);
158+
} else {
159+
assert(false);
160+
}
161+
}
162+
163+
public:
164+
static_toptree(const static_toptree_structure &stts, TreeDP &tree_dp)
165+
: stts(stts), tree_dp(tree_dp), point(stts.size()), path(stts.size()) {
166+
stts.dfs_postorder([&](int k) { _update(k); });
167+
}
168+
169+
void set(int i) {
170+
stts.path_to_root(i, [&](int k) { _update(k); });
171+
}
172+
173+
Path all_prod() const { return path.at(stts.stt_root); }
174+
175+
// Not verified yet
176+
Path get_subtree(int i) const {
177+
Path res = path.at(i);
178+
while (true) {
179+
const int p = stts.P.at(i);
180+
if (p == -1 or stts.T.at(p) != static_toptree_structure::Compress) break;
181+
182+
if (stts.L.at(p) == i) res = tree_dp.compress(res, path.at(stts.R.at(p)));
183+
i = p;
184+
}
185+
186+
return res;
187+
}
188+
};
189+
190+
/*
191+
struct tree_dp {
192+
struct Point; // Point Cluster
193+
struct Path; // Path Cluster
194+
195+
Path vertex(int i);
196+
197+
Path compress(const Path &p, const Path &c);
198+
199+
Point rake(const Point &l, const Point &r);
200+
201+
Point add_edge(const Path &d);
202+
203+
Path add_vertex(const Point &d, int i);
204+
};
205+
206+
vector<vector<int>> to(n);
207+
int root;
208+
209+
const static_toptree_structure stts(to, root);
210+
211+
tree_dp dp;
212+
static_toptree tree(stts, dp);
213+
*/

data_structure/static_toptree.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
title: Static top tree
3+
documentation_of: ./static_toptree.hpp
4+
---
5+
6+
いわゆる Static top tree を扱う.根付き木の部分木に関する各種演算をクエリあたり $O(n \log n)$ で行える.
7+
8+
## 原理
9+
10+
Static top tree のしくみについては [解説 - AtCoder Beginner Contest 351](https://atcoder.jp/contests/abc351/editorial/9868) 等も併せて参照されたい.
11+
12+
Static top tree は,根付き木をもとに平衡二分木を構築する.平衡二分木の各頂点には `PointCluster``PathCluster` の 2 種類のデータ構造のいずれかが載る.
13+
14+
例えば,入力として以下の根付き木( $0$ が根)を与えた場合に構築される static top tree を下に示す.ここで入力の木の辺のうち二重線は heavy-light decomposition (HLD) の heavy edge, 破線は light edge を示す.
15+
16+
![入力](https://gist.githubusercontent.com/hitonanode/330e4c27e0fb231f82cafaba541bbe19/raw/d9ce4a3b4fdda25e8cb9380c65ead5475cce18e6/static-top-tree-example-input-graph.svg)
17+
18+
![出力](https://gist.githubusercontent.com/hitonanode/330e4c27e0fb231f82cafaba541bbe19/raw/d9ce4a3b4fdda25e8cb9380c65ead5475cce18e6/static-top-tree-example-output-graph.svg)
19+
20+
出力の辺のうち実線は PathCluster, 破線は PointCluster が親頂点に伝播されることをそれぞれ意味する.
21+
22+
図から分かるように,HLD の heavy path は PathCluster として管理され, compress 処理によってマージされる.また, light edge で親と繋がる子頂点たちは PointCluster として管理され, rake 処理によってマージされる.これらの処理を問題に応じてうまく考案・実装してやる必要がある.
23+
24+
## 使用方法
25+
26+
まず,以下のように `Point` クラス・ `Path` クラスと `vertex()` / `compress()` / `rake()` / `add_edge()` / `add_vertex()` メソッドを持つクラスを定義する( `static` はなくても可).
27+
28+
```cpp
29+
struct tree_dp {
30+
// Point Cluster
31+
struct Point {};
32+
33+
// Path Cluster
34+
struct Path {};
35+
36+
Path vertex(int i);
37+
38+
static Path compress(const Path &parent, const Path &child);
39+
40+
static Point rake(const Point &l, const Point &r);
41+
42+
static Point add_edge(const Path &d);
43+
44+
Path add_vertex(const Point &d, int i);
45+
};
46+
```
47+
48+
その後,以下の手順で構築・利用する.
49+
50+
```cpp
51+
// 前準備
52+
vector<vector<int>> to; // 隣接リスト
53+
int root = 0;
54+
55+
// 構築
56+
const static_toptree_structure stts(to, root);
57+
tree_dp dp;
58+
static_toptree tree(stts, dp);
59+
60+
// 利用
61+
tree.set(u); // 頂点 u に更新があった場合に呼ぶ
62+
63+
auto p = tree.all_prod(); // 根に関して値取得
64+
```
65+
66+
## 問題例
67+
68+
- [AtCoder Beginner Contest 351 G - Hash on Tree](https://atcoder.jp/contests/abc351/tasks/abc351_g)
69+
- [Library Checker: Point Set Tree Path Composite Sum (Fixed Root)](https://judge.yosupo.jp/problem/point_set_tree_path_composite_sum_fixed_root)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/point_set_tree_path_composite_sum_fixed_root"
2+
3+
#include "../static_toptree.hpp"
4+
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
9+
#include <atcoder/modint>
10+
using mint = atcoder::modint998244353;
11+
12+
struct tree_dp {
13+
vector<int> A;
14+
vector<mint> parB;
15+
vector<mint> parC;
16+
17+
// Point Cluster
18+
struct Point {
19+
mint n;
20+
mint sum;
21+
};
22+
23+
// Path Cluster
24+
struct Path {
25+
mint n;
26+
mint sum;
27+
28+
mint b;
29+
mint c;
30+
};
31+
32+
Path vertex(int i) { return {1, A.at(i) * parB.at(i) + parC.at(i), parB.at(i), parC.at(i)}; }
33+
34+
static Path compress(const Path &p, const Path &c) {
35+
return {p.n + c.n, p.sum + c.sum * p.b + c.n * p.c, c.b * p.b, p.c + c.c * p.b};
36+
}
37+
38+
static Point rake(const Point &l, const Point &r) { return {l.n + r.n, l.sum + r.sum}; }
39+
40+
static Point add_edge(const Path &d) { return {d.n, d.sum}; }
41+
42+
Path add_vertex(const Point &d, int i) {
43+
return {d.n + 1, (d.sum + A.at(i)) * parB.at(i) + (d.n + 1) * parC.at(i), parB.at(i),
44+
parC.at(i)};
45+
}
46+
};
47+
48+
int main() {
49+
cin.tie(nullptr), ios::sync_with_stdio(false);
50+
51+
int N, Q;
52+
cin >> N >> Q;
53+
54+
vector<int> A(N);
55+
for (auto &a : A) cin >> a;
56+
57+
vector<int> U(N - 1), V(N - 1), B(N - 1), C(N - 1);
58+
vector<vector<int>> to(N);
59+
60+
for (int e = 0; e < N - 1; ++e) {
61+
cin >> U.at(e) >> V.at(e) >> B.at(e) >> C.at(e);
62+
to.at(U.at(e)).push_back(V.at(e));
63+
to.at(V.at(e)).push_back(U.at(e));
64+
}
65+
66+
const static_toptree_structure stts(to, 0);
67+
68+
vector<mint> parB(N, 1);
69+
vector<mint> parC(N, 0);
70+
71+
for (int e = 0; e < N - 1; ++e) {
72+
int u = U.at(e), v = V.at(e);
73+
if (stts.par.at(u) != v) swap(u, v);
74+
75+
assert(stts.par.at(u) == v);
76+
77+
parB.at(u) = B.at(e);
78+
parC.at(u) = C.at(e);
79+
}
80+
81+
tree_dp dp{A, parB, parC};
82+
83+
static_toptree tree(stts, dp);
84+
85+
while (Q--) {
86+
int tp;
87+
cin >> tp;
88+
if (tp == 0) {
89+
int w, x;
90+
cin >> w >> x;
91+
dp.A.at(w) = x;
92+
tree.set(w);
93+
} else {
94+
int e, y, z;
95+
cin >> e >> y >> z;
96+
97+
int u = U.at(e), v = V.at(e);
98+
if (stts.par.at(u) != v) swap(u, v);
99+
assert(stts.par.at(u) == v);
100+
101+
dp.parB.at(u) = y;
102+
dp.parC.at(u) = z;
103+
tree.set(u);
104+
}
105+
106+
cout << tree.all_prod().sum.val() << '\n';
107+
}
108+
}

0 commit comments

Comments
 (0)