Skip to content

Commit 1da8f32

Browse files
lrvideckisweb-flow
andauthored
New kth par alg (#219)
* renaming to hagerup * [auto-verifier] verify commit 9cd3887 * adding new kth par alg * remove chec * [auto-verifier] verify commit c90fd5c * fix tle * [auto-verifier] verify commit 33724dc --------- Co-authored-by: GitHub <noreply@github.com>
1 parent a95cc79 commit 1da8f32

8 files changed

Lines changed: 176 additions & 55 deletions

File tree

.verify-helper/timestamps.remote.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"tests/library_checker_aizu_tests/handmade_tests/functional_graph.test.cpp": "2026-04-26 11:38:07 -0600",
8282
"tests/library_checker_aizu_tests/handmade_tests/hilbert_mos.test.cpp": "2026-01-18 02:20:40 +0000",
8383
"tests/library_checker_aizu_tests/handmade_tests/kd_bit.test.cpp": "2026-02-19 18:06:52 -0700",
84+
"tests/library_checker_aizu_tests/handmade_tests/kth_par.test.cpp": "2026-06-27 13:00:50 -0500",
8485
"tests/library_checker_aizu_tests/handmade_tests/manacher.test.cpp": "2026-04-26 11:38:07 -0600",
8586
"tests/library_checker_aizu_tests/handmade_tests/merge_st_and_wavelet.test.cpp": "2026-04-26 11:38:07 -0600",
8687
"tests/library_checker_aizu_tests/handmade_tests/mobius.test.cpp": "2025-02-10 14:50:36 -0700",
@@ -141,8 +142,9 @@
141142
"tests/library_checker_aizu_tests/trees/hld_lib_checker_path.test.cpp": "2026-04-12 16:21:27 -0600",
142143
"tests/library_checker_aizu_tests/trees/hld_lib_checker_subtree_edges.test.cpp": "2026-04-12 16:21:27 -0600",
143144
"tests/library_checker_aizu_tests/trees/hld_lib_checker_subtree_nodes.test.cpp": "2026-04-12 16:21:27 -0600",
144-
"tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp": "2026-04-26 17:47:31 -0600",
145-
"tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp": "2026-04-26 11:38:07 -0600",
145+
"tests/library_checker_aizu_tests/trees/kth_path_hagerup.test.cpp": "2026-06-27 12:35:34 -0500",
146+
"tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp": "2026-06-27 12:35:34 -0500",
147+
"tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp": "2026-06-27 12:35:34 -0500",
146148
"tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp": "2026-04-26 11:38:07 -0600",
147149
"tests/library_checker_aizu_tests/trees/lca_all_methods_aizu.test.cpp": "2026-04-26 11:38:07 -0600",
148150
"tests/library_checker_aizu_tests/trees/lca_all_methods_lib_checker.test.cpp": "2026-04-26 11:38:07 -0600",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
//! https://codeforces.com/blog/entry/126580
3+
//! @code
4+
//! vector<basic_string<int>> g(n);
5+
//! hagerup kp(g);
6+
//! kp.kth_par(v, k); // k edges up from v
7+
//! kp.kth_par(v, 1); // v's parent
8+
//! @endcode
9+
//! @time O(n*max((2*K+3)/K,2*K) + q)
10+
//! @space O(n*max((2*K+3)/K,2*K))
11+
template<int K = 2> struct hagerup {
12+
int n;
13+
vi d, leaf, pos, jmp;
14+
vector<vi> lad;
15+
hagerup(const auto& g):
16+
n(sz(g)), d(n), leaf(n), pos(n), jmp(2 * n), lad(n) {
17+
static_assert(K >= 1);
18+
int t = 1;
19+
vi st(n);
20+
auto calc = [&](int s) {
21+
jmp[t] = st[max(0, s - K * (t & -t))];
22+
t++;
23+
};
24+
auto dfs = [&](auto dfs, int u, int p) -> void {
25+
int& l = leaf[u] = st[d[u]] = u;
26+
pos[u] = t;
27+
calc(d[u]);
28+
for (int v : g[u])
29+
if (v != p) {
30+
d[v] = 1 + d[u];
31+
dfs(dfs, v, u);
32+
if (d[l] < d[leaf[v]]) l = leaf[v];
33+
calc(d[u]);
34+
}
35+
int s = (d[l] - d[u]) * (2 * K + 3) / K;
36+
s = min(max(s, 2 * K), d[l] + 1);
37+
rep(i, sz(lad[l]), s) lad[l].push_back(st[d[l] - i]);
38+
};
39+
dfs(dfs, 0, 0);
40+
}
41+
int kth_par(int u, int k) {
42+
assert(0 <= k && k <= d[u]);
43+
int anc_d = d[u] - k;
44+
if (int j = bit_floor(k / (K + 1u)))
45+
u = jmp[(pos[u] & -j) | j];
46+
return u = leaf[u], lad[u][d[u] - anc_d];
47+
}
48+
};

library/trees/uncommon/ladder_decomposition.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ struct ladder {
3030
if (sz(x) > sz(path)) swap(x, path);
3131
for (int y : x) idx[y] = i;
3232
for (int y : x) lad[i++] = y;
33-
rep(j, 0, min<int>(sz(x), d[v]))
34-
lad[i++] = st[d[u] - j];
33+
rep(k, 0, min<int>(sz(x), d[v]))
34+
lad[i++] = st[d[u] - k];
3535
}
3636
path.push_back(u);
3737
return path;
Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,56 @@
11
#pragma once
2-
//! https://codeforces.com/blog/entry/126580
2+
//! https://link.springer.com/chapter/10.1007/978-3-032-29003-8_29
33
//! @code
44
//! vector<basic_string<int>> g(n);
55
//! linear_kth_par kp(g);
66
//! kp.kth_par(v, k); // k edges up from v
77
//! kp.kth_par(v, 1); // v's parent
88
//! @endcode
9-
//! @time O(n*max((2*K+3)/K,2*K) + q)
10-
//! @space O(n*max((2*K+3)/K,2*K))
11-
template<int K = 2> struct linear_kth_par {
9+
//! @time O(n + q)
10+
//! @space O(n)
11+
struct linear_kth_par {
1212
int n;
13-
vi d, leaf, pos, jmp;
14-
vector<vi> lad;
13+
vi d, et_i, et_d, idx, lad;
14+
vector<basic_string<int>> jmp;
1515
linear_kth_par(const auto& g):
16-
n(sz(g)), d(n), leaf(n), pos(n), jmp(2 * n), lad(n) {
17-
static_assert(K >= 1);
18-
int t = 1;
16+
n(sz(g)), d(n), et_i(n), et_d(2 * n), idx(n),
17+
lad(2 * n), jmp(2 * n) {
18+
int i = 0, j = 1;
1919
vi st(n);
20-
auto calc = [&](int s) {
21-
jmp[t] = st[max(0, s - K * (t & -t))];
22-
t++;
20+
auto calc = [&](int u) {
21+
et_d[et_i[u] = j] = d[u];
22+
for (int k = 1; k <= min(j & -j, d[u]); k *= 2)
23+
jmp[j] += st[d[u] - k];
24+
j++;
2325
};
24-
auto dfs = [&](auto dfs, int u, int p) -> void {
25-
int& l = leaf[u] = st[d[u]] = u;
26-
pos[u] = t;
27-
calc(d[u]);
26+
auto dfs = [&](auto dfs, int u, int p) -> vi {
27+
vi path;
28+
st[d[u]] = u;
29+
calc(u);
2830
for (int v : g[u])
2931
if (v != p) {
30-
d[v] = 1 + d[u];
31-
dfs(dfs, v, u);
32-
if (d[l] < d[leaf[v]]) l = leaf[v];
33-
calc(d[u]);
32+
d[v] = d[u] + 1;
33+
vi x = dfs(dfs, v, u);
34+
calc(u);
35+
if (sz(x) > sz(path)) swap(x, path);
36+
for (int y : x) idx[y] = i;
37+
for (int y : x) lad[i++] = y;
38+
rep(k, 0, min<int>(sz(x), d[v]))
39+
lad[i++] = st[d[u] - k];
3440
}
35-
int s = (d[l] - d[u]) * (2 * K + 3) / K;
36-
s = min(max(s, 2 * K), d[l] + 1);
37-
rep(i, sz(lad[l]), s) lad[l].push_back(st[d[l] - i]);
41+
path.push_back(u);
42+
return path;
3843
};
39-
dfs(dfs, 0, 0);
44+
vi x = dfs(dfs, 0, 0);
45+
for (int y : x) idx[y] = i;
46+
for (int y : x) lad[i++] = y;
4047
}
4148
int kth_par(int u, int k) {
4249
assert(0 <= k && k <= d[u]);
43-
int anc_d = d[u] - k;
44-
if (int j = bit_floor(k / (K + 1u)))
45-
u = jmp[(pos[u] & -j) | j];
46-
return u = leaf[u], lad[u][d[u] - anc_d];
50+
if (k == 0) return u;
51+
int anc_d = d[u] - k, bc = bit_ceil(k + 0u);
52+
int j = (et_i[u] + bc / 2) & -bc;
53+
int i = idx[jmp[j].at(__lg(et_d[j] - anc_d))];
54+
return lad[i + d[lad[i]] - anc_d];
4755
}
4856
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#define PROBLEM \
2+
"https://onlinejudge.u-aizu.ac.jp/problems/ITP1_1_A"
3+
#include "../template.hpp"
4+
#include "../../../library/contest/random.hpp"
5+
#include "../../../library/trees/uncommon/hagerup_kth_par.hpp"
6+
#include "../../../library/trees/uncommon/linear_kth_par.hpp"
7+
int main() {
8+
for (int n = 1; n <= 100; n++) {
9+
for (int param = 1; param < 20; param++) {
10+
vector<int> par(n);
11+
vector<vector<int>> adj(n);
12+
for (int i = 1; i < n; i++) {
13+
if (param == 19) par[i] = rnd(0, i - 1);
14+
else par[i] = rnd(max(0, i - param), i - 1);
15+
adj[par[i]].push_back(i);
16+
adj[i].push_back(par[i]);
17+
}
18+
hagerup<2> hagerup_kth_par(adj);
19+
linear_kth_par lin_kth_par(adj);
20+
for (int i = 0; i < n; i++) {
21+
for (int k = 0, kth_parent_naive = i;
22+
k <= hagerup_kth_par.d[i];
23+
k++, kth_parent_naive = par[kth_parent_naive]) {
24+
assert(kth_parent_naive ==
25+
hagerup_kth_par.kth_par(i, k));
26+
assert(
27+
kth_parent_naive == lin_kth_par.kth_par(i, k));
28+
}
29+
}
30+
}
31+
}
32+
cout << "Hello World\n";
33+
return 0;
34+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#define PROBLEM \
2+
"https://judge.yosupo.jp/problem/jump_on_tree"
3+
#include "../template.hpp"
4+
#include "../../../library/trees/linear_lca.hpp"
5+
#include "../../../library/trees/uncommon/hagerup_kth_par.hpp"
6+
#include "../../../library/trees/lca_rmq.hpp"
7+
#include "../compress_tree_asserts.hpp"
8+
int main() {
9+
cin.tie(0)->sync_with_stdio(0);
10+
int n, q;
11+
cin >> n >> q;
12+
vector<vector<int>> adj(n);
13+
for (int i = 0; i < n - 1; i++) {
14+
int u, v;
15+
cin >> u >> v;
16+
adj[u].push_back(v);
17+
adj[v].push_back(u);
18+
}
19+
linear_lca lin_lca(adj);
20+
hagerup<1> lin_kp_1(adj);
21+
hagerup<2> lin_kp_2(adj);
22+
hagerup<3> lin_kp_3(adj);
23+
hagerup<4> lin_kp_4(adj);
24+
LCA lc(adj);
25+
compress_tree_asserts(adj, lc);
26+
auto get_kth_par = [&](int v, int k) -> int {
27+
int res = lin_kp_1.kth_par(v, k);
28+
assert(res == lin_kp_2.kth_par(v, k));
29+
assert(res == lin_kp_3.kth_par(v, k));
30+
assert(res == lin_kp_4.kth_par(v, k));
31+
return res;
32+
};
33+
while (q--) {
34+
int u, v, k;
35+
cin >> u >> v >> k;
36+
int lca_d = lin_kp_2.d[lin_lca.lca(u, v)];
37+
int u_lca = lin_kp_2.d[u] - lca_d;
38+
int v_lca = lin_kp_2.d[v] - lca_d;
39+
if (k <= u_lca) cout << get_kth_par(u, k) << '\n';
40+
else if (k <= u_lca + v_lca)
41+
cout << get_kth_par(v, u_lca + v_lca - k) << '\n';
42+
else cout << -1 << '\n';
43+
}
44+
}

tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
#include "../template.hpp"
44
#include "../../../library/trees/linear_lca.hpp"
55
#include "../../../library/trees/uncommon/linear_kth_par.hpp"
6-
#include "../../../library/trees/lca_rmq.hpp"
7-
#include "../compress_tree_asserts.hpp"
86
int main() {
97
cin.tie(0)->sync_with_stdio(0);
108
int n, q;
@@ -17,28 +15,18 @@ int main() {
1715
adj[v].push_back(u);
1816
}
1917
linear_lca lin_lca(adj);
20-
linear_kth_par<1> lin_kp_1(adj);
21-
linear_kth_par<2> lin_kp_2(adj);
22-
linear_kth_par<3> lin_kp_3(adj);
23-
linear_kth_par<4> lin_kp_4(adj);
24-
LCA lc(adj);
25-
compress_tree_asserts(adj, lc);
26-
auto get_kth_par = [&](int v, int k) -> int {
27-
int res = lin_kp_1.kth_par(v, k);
28-
assert(res == lin_kp_2.kth_par(v, k));
29-
assert(res == lin_kp_3.kth_par(v, k));
30-
assert(res == lin_kp_4.kth_par(v, k));
31-
return res;
32-
};
18+
linear_kth_par lin_kth_par(adj);
3319
while (q--) {
3420
int u, v, k;
3521
cin >> u >> v >> k;
36-
int lca_d = lin_kp_2.d[lin_lca.lca(u, v)];
37-
int u_lca = lin_kp_2.d[u] - lca_d;
38-
int v_lca = lin_kp_2.d[v] - lca_d;
39-
if (k <= u_lca) cout << get_kth_par(u, k) << '\n';
22+
int lca_d = lin_lca.d[lin_lca.lca(u, v)];
23+
int u_lca = lin_lca.d[u] - lca_d;
24+
int v_lca = lin_lca.d[v] - lca_d;
25+
if (k <= u_lca)
26+
cout << lin_kth_par.kth_par(u, k) << '\n';
4027
else if (k <= u_lca + v_lca)
41-
cout << get_kth_par(v, u_lca + v_lca - k) << '\n';
28+
cout << lin_kth_par.kth_par(v, u_lca + v_lca - k)
29+
<< '\n';
4230
else cout << -1 << '\n';
4331
}
4432
}

tests/scripts/grep_clangformat_cppcheck.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ grep "ssize" --recursive ../library/ && exit 1
2626
echo "check vi instead of vector<int>"
2727
grep "vector<int>" --recursive ../library/**/*.hpp && exit 1
2828

29-
echo "check no basic_string, excluding @code example inits"
30-
grep "[[:space:]]*//\!" --recursive --invert-match ../library/**/*.hpp library_checker_aizu_tests/**/*.test.cpp | grep "basic_string" && exit 1
31-
3229
echo "check begin(arr) instead of arr.begin(), similarly for end, rbegin, rend, empty, size:"
3330
# TODO: remove this define filter if/when we move to -std=c++20
3431
grep --invert-match --fixed-strings "#define" --recursive ../library/ library_checker_aizu_tests/ |

0 commit comments

Comments
 (0)