Skip to content

Commit 635e077

Browse files
committed
Add Schieber-Vishkin LCA and reorganize verify layout
1 parent 7cf9adc commit 635e077

5 files changed

Lines changed: 190 additions & 1 deletion

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
tags:
3+
- tree
4+
- lca
5+
---
6+
7+
# Schieber-Vishkin LCA
8+
9+
木上の LCA を `build: O(N)`, `query: O(1)` で処理する実装です。
10+
11+
## Interface
12+
13+
```cpp
14+
SchieberVishkinLCA lca(n);
15+
lca.add_edge(u, v);
16+
lca.build(root);
17+
int a = lca.lca(u, v);
18+
```
19+
20+
## Verify
21+
22+
- [Library Checker - Lowest Common Ancestor](https://judge.yosupo.jp/problem/lca)
23+
- `verify/tree/schieber_vishkin_lca/library_checker_lca.test.cpp`
24+
25+
## Code
26+
27+
```cpp
28+
--8<-- "include/tree/schieber_vishkin_lca.hpp"
29+
```
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <bit>
5+
#include <vector>
6+
7+
struct SchieberVishkinLCA {
8+
using uint = unsigned int;
9+
10+
static uint lowbit(uint x) { return x & (~x + 1); }
11+
static uint msb_minus_1_mask(uint x) { return std::bit_floor(x | 1u) - 1u; }
12+
13+
int n;
14+
int root = -1;
15+
std::vector<std::vector<int>> g;
16+
std::vector<int> ord, par;
17+
std::vector<uint> idx, inlabel, ascendant;
18+
std::vector<int> head;
19+
20+
explicit SchieberVishkinLCA(int n = 0) : n(n), g(n), par(n, -1), idx(n, 0) { ord.reserve(n); }
21+
22+
void add_edge(int x, int y) {
23+
g[x].push_back(y);
24+
g[y].push_back(x);
25+
}
26+
27+
// build: O(N), query: O(1)
28+
void build(int r = 0) {
29+
if (n == 0) return;
30+
31+
ord.clear();
32+
std::fill(par.begin(), par.end(), -1);
33+
root = r;
34+
35+
std::vector<int> st;
36+
st.reserve(n);
37+
st.push_back(r);
38+
par[r] = -1;
39+
40+
while (!st.empty()) {
41+
int v = st.back();
42+
st.pop_back();
43+
ord.push_back(v);
44+
for (int i = (int)g[v].size() - 1; i >= 0; --i) {
45+
int to = g[v][i];
46+
if (to == par[v]) continue;
47+
par[to] = v;
48+
st.push_back(to);
49+
}
50+
}
51+
52+
for (int i = 0; i < n; ++i) {
53+
idx[ord[i]] = (uint)(i + 1);
54+
}
55+
56+
inlabel = idx;
57+
for (int i = n - 1; i > 0; --i) {
58+
int v = ord[i];
59+
int p = par[v];
60+
if (lowbit(inlabel[p]) < lowbit(inlabel[v])) inlabel[p] = inlabel[v];
61+
}
62+
63+
ascendant.assign(n, 0);
64+
head.assign(n, 0);
65+
66+
ascendant[r] = 0;
67+
head[0] = r;
68+
69+
for (int i = 1; i < n; ++i) {
70+
int v = ord[i];
71+
int p = par[v];
72+
ascendant[v] = ascendant[p] | lowbit(inlabel[v]);
73+
}
74+
for (int i = 1; i < n; ++i) {
75+
int v = ord[i];
76+
int p = par[v];
77+
if (inlabel[v] != inlabel[p]) {
78+
head[idx[v] - 1] = p;
79+
} else {
80+
head[idx[v] - 1] = head[idx[p] - 1];
81+
}
82+
}
83+
}
84+
85+
int lca(int u, int v) const {
86+
uint Iv = inlabel[v];
87+
uint Iu = inlabel[u];
88+
uint hIv = lowbit(Iv);
89+
uint hIu = lowbit(Iu);
90+
uint mask = msb_minus_1_mask((Iv ^ Iu) | hIv | hIu);
91+
uint j = lowbit(ascendant[v] & ascendant[u] & ~mask);
92+
93+
int x, y;
94+
if (j == hIv) {
95+
x = v;
96+
} else {
97+
mask = msb_minus_1_mask(ascendant[v] & (j - 1));
98+
x = head[(idx[v] & ~mask | (mask + 1)) - 1];
99+
}
100+
if (j == hIu) {
101+
y = u;
102+
} else {
103+
mask = msb_minus_1_mask(ascendant[u] & (j - 1));
104+
y = head[(idx[u] & ~mask | (mask + 1)) - 1];
105+
}
106+
return idx[x] < idx[y] ? x : y;
107+
}
108+
};

mkdocs.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ markdown_extensions:
6161
linenums: true # コードブロックに行番号を表示
6262
- pymdownx.inlinehilite # 行内コード ==var== のように書くとハイライト色付きで強調。
6363
- pymdownx.superfences # トリプルフェンス内でタブ切替 (=== "C++" など) を実装。複合的なコードブロックや Markdown in code も可。
64+
- pymdownx.snippets:
65+
check_paths: true
66+
base_path:
67+
- .
6468
- pymdownx.arithmatex: # $ ... $ / $$ ... $$ で LaTeX 数式を埋め込む。
6569
generic: true
6670
- codehilite: # Python-Markdown 標準のハイライト拡張。
@@ -74,4 +78,5 @@ nav:
7478
- Library:
7579
- sample: library/sample/sample.md
7680
- Bell: library/graph/bellman_ford.md
77-
- Note: note/note.md
81+
- Schieber-Vishkin LCA: library/tree/schieber_vishkin_lca.md
82+
- Note: note/note.md

verify/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# verify 方針(暫定)
2+
3+
- verify コードは `verify/<category>/<library_name>/` に置く。
4+
- 1ライブラリに対して複数ジャッジの verify を並べる。
5+
- 例: `library_checker_lca.test.cpp`, `atcoder_abc014_d.test.cpp`
6+
- verify コードは `include/...` のライブラリを `#include` して使う。
7+
- ライブラリ内部を変更しても、公開インタフェースが同じなら verify コードは原則そのままにする。
8+
9+
## 運用フロー
10+
11+
1. ライブラリを変更する。
12+
2. verify コードに対して、提出用コードを生成(`#include` 展開・コピー)する。
13+
3. 提出先に手動提出する。
14+
4. AC を確認できたら、手動で verify 済みとしてマークする。
15+
16+
## Schieber-Vishkin LCA
17+
18+
- 問題: https://judge.yosupo.jp/problem/lca
19+
- コード: `verify/tree/schieber_vishkin_lca/library_checker_lca.test.cpp`
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// verification-helper: PROBLEM https://judge.yosupo.jp/problem/lca
2+
3+
#include <iostream>
4+
5+
#include "../../../include/tree/schieber_vishkin_lca.hpp"
6+
7+
int main() {
8+
std::ios::sync_with_stdio(false);
9+
std::cin.tie(nullptr);
10+
11+
int n, q;
12+
std::cin >> n >> q;
13+
14+
SchieberVishkinLCA lca(n);
15+
for (int i = 1; i < n; ++i) {
16+
int p;
17+
std::cin >> p;
18+
lca.add_edge(i, p);
19+
}
20+
lca.build(0);
21+
22+
while (q--) {
23+
int u, v;
24+
std::cin >> u >> v;
25+
std::cout << lca.lca(u, v) << '\n';
26+
}
27+
return 0;
28+
}

0 commit comments

Comments
 (0)