Skip to content

Commit 6afb86c

Browse files
authored
Merge pull request #367 from hitonanode/update-euler-tour
Update euler tour
2 parents 01d9f22 + d735518 commit 6afb86c

9 files changed

+208
-42
lines changed

graph/extended_block_cut_trees.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@ hld.build();
5151
5252
- [Block-cut tree - ei1333の日記](https://ei1333.hateblo.jp/entry/2020/03/25/010057)
5353
- [My Algorithm : kopricky アルゴリズムライブラリ](https://kopricky.github.io/code/GraphDecomposition/articulation.html)
54-
- [拡張 Block Cut Tree | cp_library](https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html)
54+
- [拡張 Block Cut Tree \| cp_library](https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html)
5555
- [Xユーザーの熨斗袋さん: 「G = (V, E) に対する Block Cut Tree、G の2-点連結成分全体を C として...](https://x.com/noshi91/status/1529858538650374144)

other_algorithms/mos_algorithm.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ for (auto x : ret) cout << x << '\n';
2929
## 問題例
3030
3131
- [Codeforces Round #221 (Div. 1) D. Tree and Queries](https://codeforces.com/contest/375/submission/114665433)
32-
- 部分木クエリにも使用可能.
32+
- 部分木クエリにも使用可能(ただし DSU on tree でもより良い最悪計算量で同様のことが可能)
3333
3434
## Links
3535
3636
- [Codeforces blog のコメント](https://codeforces.com/blog/entry/20032?#comment-248430) : "Mo's algorithm" の起源に関する議論.
37+
- [Mo's algorithm - ei1333の日記](https://ei1333.hateblo.jp/entry/2017/09/11/211011)
38+
- 木の部分木クエリやパスクエリに対する Mo's algorithm の応用にも触れられている.

tree/euler_tour.hpp

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <utility>
4+
#include <vector>
5+
6+
// Euler tour
7+
// https://maspypy.com/euler-tour-%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B7
8+
struct euler_tour {
9+
int n;
10+
int root;
11+
12+
std::vector<std::pair<int, int>> edges; // (parent, child)
13+
14+
// - 頂点 v に関する部分木に含まれる辺は, [begins[v], ends[v]) に 2 回ずつ登場
15+
// - [begins[u], begins[v]) (begins[u] <= begins[v]) の半開区間に u-v パスを構成する辺が奇数回登場
16+
std::vector<int> begins;
17+
std::vector<int> ends;
18+
19+
std::vector<int> par_eid;
20+
std::vector<std::pair<int, bool>> tour; // (edge_id, flg) flg=true: down, false: up
21+
22+
void _build_dfs(int now, int prv_eid, const std::vector<std::vector<std::pair<int, int>>> &to) {
23+
tour.emplace_back(prv_eid, true);
24+
begins[now] = tour.size();
25+
26+
for (auto [nxt, eid] : to[now]) {
27+
if (eid == prv_eid) continue;
28+
par_eid[nxt] = eid;
29+
if (edges[eid].first == nxt) std::swap(edges[eid].first, edges[eid].second);
30+
_build_dfs(nxt, eid, to);
31+
}
32+
33+
ends[now] = tour.size();
34+
tour.emplace_back(prv_eid, false);
35+
}
36+
37+
euler_tour() = default;
38+
39+
euler_tour(int n, const std::vector<std::pair<int, int>> &edges_, int root)
40+
: n(n), root(root), edges(edges_), begins(n, -1), ends(n, -1), par_eid(n, -1) {
41+
std::vector<std::vector<std::pair<int, int>>> to(n);
42+
for (int eid = 0; eid < (int)edges.size(); ++eid) {
43+
auto [u, v] = edges[eid];
44+
assert(u != v);
45+
to.at(u).emplace_back(v, eid);
46+
to.at(v).emplace_back(u, eid);
47+
}
48+
49+
_build_dfs(root, -1, to);
50+
}
51+
52+
// 頂点 v の部分木の頂点数
53+
int subtree_size(int v) const { return (ends.at(v) - begins.at(v)) / 2 + 1; }
54+
55+
int par(int v) const {
56+
int eid = par_eid.at(v);
57+
return eid == -1 ? -1 : edges[eid].first;
58+
}
59+
60+
int tour_child(int idx) const {
61+
int eid = tour.at(idx).first;
62+
return eid < 0 ? root : edges[eid].second;
63+
}
64+
};

tree/euler_tour.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
title: Euler tour (木のオイラーツアー)
3+
documentation_of: ./euler_tour.hpp
4+
---
5+
6+
木のオイラーツアーを構築.パスクエリをデータ構造に載せて高速に解く場合や, Mo's algorithm を適用する場合に有用.
7+
8+
## 使用方法
9+
10+
以下で構築できる.
11+
12+
```cpp
13+
int n, root;
14+
vector<pair<int, int>> edges;
15+
16+
const euler_tour et(n, edges, root);
17+
```
18+
19+
木上の $u$-$v$ パスに着目したい場合,以下の処理を行うと,当該パスを構成する辺が半開区間 `[begins, ends)` に丁度奇数回登場する.
20+
21+
```cpp
22+
int begins = et.begins.at(u), ends = et.begins.at(v);
23+
24+
for (int i = begins; i < ends; ++i) {
25+
// Insert or erase et.tour_child(i);
26+
}
27+
```
28+
29+
半開区間 `[begins, ends)` たちに対して Mo's algorithm などを適用することで,クエリたちが効率的に処理できることがある.
30+
31+
## 問題例
32+
33+
- [Primitive Queries \| CodeChef](https://www.codechef.com/problems/DISTNUM3)
34+
- 木のパス上の頂点集合に関するクエリを Mo's algorithm で解く.
35+
36+
## 参考文献・リンク
37+
38+
- [Mo's algorithm - ei1333の日記](https://ei1333.hateblo.jp/entry/2017/09/11/211011)

tree/eulertour.hpp

-30
This file was deleted.

tree/preorder_tree_dfs.hpp

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <vector>
4+
5+
// Preorder tree DFS
6+
// 木を DFS して行きがけ順に頂点を保持する.
7+
// 木を区間に変換する.部分木を構成する頂点は連続して現れるので,部分木の頂点クエリ等に有用.
8+
// heavy_light_decomposition が上位互換
9+
struct preorder_tree_dfs {
10+
int n; // # of vertices of tree
11+
int root;
12+
std::vector<int> subtree_begin, subtree_end;
13+
std::vector<int> vis_order;
14+
15+
void _build_dfs(int now, const std::vector<std::vector<int>> &to) {
16+
subtree_begin[now] = vis_order.size();
17+
vis_order.push_back(now);
18+
for (auto nxt : to[now]) {
19+
if (subtree_begin[nxt] == -1) _build_dfs(nxt, to);
20+
}
21+
subtree_end[now] = vis_order.size();
22+
}
23+
24+
preorder_tree_dfs() = default;
25+
26+
preorder_tree_dfs(const std::vector<std::vector<int>> &to, int root)
27+
: n(to.size()), root(root), subtree_begin(n, -1), subtree_end(n, -1) {
28+
_build_dfs(root, to);
29+
}
30+
};

tree/preorder_tree_dfs.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Preorder tree DFS (木の行きがけ順 DFS)
3+
documentation_of: ./preorder_tree_dfs.hpp
4+
---
5+
6+
与えられた木に対して,行きがけ順に頂点を列挙した列を構築する.部分木を構成する頂点は連続して現れるので,部分木の頂点クエリ等に有用.
7+
8+
## 使用方法
9+
10+
```cpp
11+
int N;
12+
vector<vector<int>> to(N);
13+
int root;
14+
15+
preorder_tree_dfs tour(to, root);
16+
17+
// 頂点 v の部分木を構成する頂点は半開区間 [tour.subtree_begin[v], tour.subtree_end[v]) に現れる
18+
```
19+
20+
## 問題例
21+
22+
- [Library Checker: Vertex Add Subtree Sum](https://judge.yosupo.jp/problem/vertex_add_subtree_sum)

tree/test/preorder_tree_dfs.test.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/vertex_add_subtree_sum"
2+
#include "../preorder_tree_dfs.hpp"
3+
#include <cassert>
4+
#include <iostream>
5+
#include <atcoder/fenwicktree>
6+
using namespace std;
7+
8+
int main() {
9+
int N, Q;
10+
cin >> N >> Q;
11+
vector<long long> A(N);
12+
vector<vector<int>> to(N);
13+
for (auto &a : A) cin >> a;
14+
for (int i = 1; i < N; i++) {
15+
int p;
16+
cin >> p;
17+
to.at(p).push_back(i);
18+
to.at(i).push_back(p);
19+
}
20+
21+
preorder_tree_dfs tour(to, 0);
22+
23+
atcoder::fenwick_tree<long long> ft(N);
24+
for (int i = 0; i < N; i++) ft.add(tour.subtree_begin.at(i), A.at(i));
25+
26+
while (Q--) {
27+
int q;
28+
cin >> q;
29+
if (q) {
30+
int u;
31+
cin >> u;
32+
printf("%lld\n", ft.sum(tour.subtree_begin[u], tour.subtree_end[u]));
33+
} else {
34+
int u, x;
35+
cin >> u >> x;
36+
ft.add(tour.subtree_begin[u], x);
37+
}
38+
}
39+
}
+11-10
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
1-
#include "../../segmenttree/point-update-range-get_nonrecursive.hpp"
2-
#include "../eulertour.hpp"
31
#define PROBLEM "https://judge.yosupo.jp/problem/vertex_add_subtree_sum"
2+
#include "../heavy_light_decomposition.hpp"
43
#include <cassert>
54
#include <iostream>
5+
#include <atcoder/fenwicktree>
66
using namespace std;
77

88
int main() {
99
int N, Q;
1010
cin >> N >> Q;
1111
vector<long long> A(N);
12-
vector<vector<int>> to(N);
12+
vector<pair<int, int>> edges;
1313
for (auto &a : A) cin >> a;
1414
for (int i = 1; i < N; i++) {
1515
int p;
1616
cin >> p;
17-
to[p].push_back(i);
18-
to[i].push_back(p);
17+
edges.emplace_back(p, i);
1918
}
2019

21-
PreorderEulerTour tour(to, 0);
20+
heavy_light_decomposition tour(N, edges);
21+
tour.build();
22+
2223
vector<long long> v;
2324
for (auto i : tour.vis_order) v.push_back(A[i]);
2425
assert(int(v.size()) == N);
25-
PointUpdateRangeSum<long long> rsq(v, 0);
26+
atcoder::fenwick_tree<long long> ft(N);
27+
for (int i = 0; i < N; i++) ft.add(i, v[i]);
2628

2729
while (Q--) {
2830
int q;
2931
cin >> q;
3032
if (q) {
3133
int u;
3234
cin >> u;
33-
printf("%lld\n", rsq.get(tour.subtree_begin[u], tour.subtree_end[u]));
35+
printf("%lld\n", ft.sum(tour.subtree_begin[u], tour.subtree_end[u]));
3436
} else {
3537
int u, x;
3638
cin >> u >> x;
37-
A[u] += x;
38-
rsq.update(tour.subtree_begin[u], A[u]);
39+
ft.add(tour.subtree_begin[u], x);
3940
}
4041
}
4142
}

0 commit comments

Comments
 (0)