Skip to content

Commit 5855190

Browse files
committed
add Euler tour
1 parent af57156 commit 5855190

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

tree/euler_tour.hpp

Lines changed: 64 additions & 0 deletions
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

Lines changed: 38 additions & 0 deletions
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)

0 commit comments

Comments
 (0)