Skip to content

Commit 91b434a

Browse files
authored
Stern–Brocot tree (#322)
1 parent c85ee7f commit 91b434a

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed

number/stern_brocot_tree.hpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <utility>
5+
#include <vector>
6+
7+
// Stern–Brocot tree
8+
// Implementation based on https://miscalc.hatenablog.com/entry/2023/12/22/213007
9+
namespace SternBrocotTree {
10+
11+
using T = long long;
12+
13+
struct Node {
14+
// Subtree contains all rational numbers in (p/q, r/s)
15+
T p = 0, q = 1, r = 1, s = 0; // root is (0, \infty)
16+
17+
// (p + r) / (q + s)
18+
T num() const { return p + r; }
19+
T den() const { return q + s; }
20+
};
21+
22+
enum class Direction { Left, Right };
23+
24+
struct Move {
25+
Direction dir;
26+
T steps;
27+
};
28+
29+
Node apply(Node node, const Move &mv) {
30+
if (mv.dir == Direction::Left) {
31+
node.r += node.p * mv.steps;
32+
node.s += node.q * mv.steps;
33+
} else {
34+
node.p += node.r * mv.steps;
35+
node.q += node.s * mv.steps;
36+
}
37+
return node;
38+
}
39+
40+
// path from root to num/den
41+
std::vector<Move> encode_path(T num, T den) {
42+
std::vector<Move> ret;
43+
bool left = false;
44+
45+
while (num != den) {
46+
T steps = num / den;
47+
if (den * steps == num) --steps;
48+
num -= steps * den;
49+
if (steps) ret.push_back({left ? Direction::Left : Direction::Right, steps});
50+
51+
std::swap(num, den);
52+
left = !left;
53+
}
54+
55+
return ret;
56+
}
57+
58+
Node decode_path(const std::vector<Move> &path) {
59+
Node ret{0, 1, 1, 0};
60+
for (const Move &mv : path) ret = apply(ret, mv);
61+
62+
return ret;
63+
}
64+
65+
std::vector<Move> lca_path(const std::vector<Move> &path1, const std::vector<Move> &path2) {
66+
std::vector<Move> ret_path;
67+
68+
int i1 = 0, i2 = 0;
69+
T step1 = path1.empty() ? 0 : path1.front().steps;
70+
T step2 = path2.empty() ? 0 : path2.front().steps;
71+
72+
while (i1 < (int)path1.size() and i2 < (int)path2.size()) {
73+
if (!step1) {
74+
++i1;
75+
if (i1 < (int)path1.size()) step1 = path1.at(i1).steps;
76+
} else if (!step2) {
77+
++i2;
78+
if (i2 < (int)path2.size()) step2 = path2.at(i2).steps;
79+
} else {
80+
if (path1.at(i1).dir != path2.at(i2).dir) break;
81+
T steps = std::min(step1, step2);
82+
step1 -= steps;
83+
step2 -= steps;
84+
85+
if (ret_path.empty() or ret_path.back().dir != path1.at(i1).dir) {
86+
Move move{path1.at(i1).dir, steps};
87+
ret_path.push_back(move);
88+
} else {
89+
ret_path.back().steps += steps;
90+
}
91+
}
92+
}
93+
94+
return ret_path;
95+
}
96+
97+
} // namespace SternBrocotTree

number/stern_brocot_tree.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: Stern–Brocot tree
3+
documentation_of: ./stern_brocot_tree.hpp
4+
---
5+
6+
Stern–Brocot tree に関連する処理・探索アルゴリズム.大半の関数は与えられる整数の対数時間で動作する.
7+
8+
## 使用方法
9+
10+
```cpp
11+
long long a, b; // coprime
12+
auto path = SternBrocotTree::encode_path(a, b); // path from root (= 1) to a/b
13+
14+
auto node = SternBrocotTree::decode_path(path); // path to rational number
15+
assert(node.num() == a);
16+
assert(node.den() == b);
17+
18+
long long c, d;
19+
auto path2 = SternBrocotTree::encode_path(c, d);
20+
auto path_lca = SternBrocotTree::lca_path(path, path2); // path to LCA
21+
```
22+
23+
## 問題例
24+
25+
- [Library Checker: Stern–Brocot Tree](https://judge.yosupo.jp/problem/stern_brocot_tree)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/stern_brocot_tree"
2+
#include "../stern_brocot_tree.hpp"
3+
4+
#include <cassert>
5+
#include <iostream>
6+
#include <string>
7+
#include <vector>
8+
using namespace std;
9+
10+
int main() {
11+
cin.tie(nullptr);
12+
ios::sync_with_stdio(false);
13+
14+
int Q;
15+
cin >> Q;
16+
while (Q--) {
17+
string type;
18+
cin >> type;
19+
if (type == "ENCODE_PATH") {
20+
long long a, b;
21+
cin >> a >> b;
22+
23+
auto path = SternBrocotTree::encode_path(a, b);
24+
25+
cout << path.size();
26+
for (auto mv : path) {
27+
cout << ' ' << (mv.dir == SternBrocotTree::Direction::Left ? 'L' : 'R') << ' '
28+
<< mv.steps;
29+
}
30+
cout << '\n';
31+
} else if (type == "DECODE_PATH") {
32+
std::vector<SternBrocotTree::Move> path;
33+
int k;
34+
cin >> k;
35+
while (k--) {
36+
char dir;
37+
long long steps;
38+
cin >> dir >> steps;
39+
path.push_back({dir == 'L' ? SternBrocotTree::Direction::Left
40+
: SternBrocotTree::Direction::Right,
41+
steps});
42+
}
43+
44+
auto node = SternBrocotTree::decode_path(path);
45+
cout << node.num() << ' ' << node.den() << '\n';
46+
} else if (type == "LCA") {
47+
long long a, b, c, d;
48+
cin >> a >> b >> c >> d;
49+
50+
const auto path1 = SternBrocotTree::encode_path(a, b);
51+
const auto path2 = SternBrocotTree::encode_path(c, d);
52+
const auto path = SternBrocotTree::lca_path(path1, path2);
53+
const auto ret = SternBrocotTree::decode_path(path);
54+
cout << ret.num() << ' ' << ret.den() << '\n';
55+
} else if (type == "ANCESTOR") {
56+
long long k, a, b;
57+
cin >> k >> a >> b;
58+
59+
auto path = SternBrocotTree::encode_path(a, b);
60+
61+
SternBrocotTree::Node node;
62+
for (const auto &mv : path) {
63+
long long steps = min(mv.steps, k);
64+
k -= steps;
65+
node = SternBrocotTree::apply(node, {mv.dir, steps});
66+
}
67+
68+
if (k) {
69+
cout << "-1\n";
70+
} else {
71+
cout << node.num() << ' ' << node.den() << '\n';
72+
}
73+
} else if (type == "RANGE") {
74+
long long a, b;
75+
cin >> a >> b;
76+
77+
auto path = SternBrocotTree::encode_path(a, b);
78+
auto node = SternBrocotTree::decode_path(path);
79+
cout << node.p << ' ' << node.q << ' ' << node.r << ' ' << node.s << '\n';
80+
} else {
81+
assert(false);
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)