Skip to content

Commit 01d01a9

Browse files
lrvideckisweb-flow
andauthored
Edge cd modernize (#197)
* modernize edge CD * [auto-verifier] verify commit a3ec772 * fix docs * [auto-verifier] verify commit 5833c5d * fix tests * [auto-verifier] verify commit 0f27055 --------- Co-authored-by: GitHub <noreply@github.com>
1 parent fd63cc7 commit 01d01a9

File tree

10 files changed

+165
-175
lines changed

10 files changed

+165
-175
lines changed

.verify-helper/timestamps.remote.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@
7171
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_aizu.test.cpp": "2026-01-17 13:05:42 -0700",
7272
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_lib_checker.test.cpp": "2026-01-17 13:05:42 -0700",
7373
"tests/library_checker_aizu_tests/graphs/two_edge_components.test.cpp": "2026-02-27 11:16:07 -0700",
74-
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-03-16 14:02:06 -0600",
74+
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-03-16 14:23:41 -0600",
7575
"tests/library_checker_aizu_tests/handmade_tests/dsu.test.cpp": "2026-01-22 10:08:22 -0700",
76-
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2026-03-16 13:39:47 -0600",
76+
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2026-03-16 14:23:41 -0600",
7777
"tests/library_checker_aizu_tests/handmade_tests/fib_matrix_expo.test.cpp": "2026-01-28 21:48:16 -0700",
7878
"tests/library_checker_aizu_tests/handmade_tests/functional_graph.test.cpp": "2025-08-06 16:18:37 -0600",
7979
"tests/library_checker_aizu_tests/handmade_tests/hilbert_mos.test.cpp": "2026-01-18 02:20:40 +0000",
@@ -128,11 +128,11 @@
128128
"tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp": "2026-01-28 01:00:19 -0700",
129129
"tests/library_checker_aizu_tests/strings/trie.test.cpp": "2026-01-17 12:38:18 -0700",
130130
"tests/library_checker_aizu_tests/strings/wildcard_pattern_matching.test.cpp": "2025-08-05 19:19:23 -0600",
131-
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2026-03-16 13:39:47 -0600",
132-
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2026-03-16 13:39:47 -0600",
133-
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2026-03-16 13:39:47 -0600",
134-
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2026-03-16 13:39:47 -0600",
135-
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2026-03-16 13:39:47 -0600",
131+
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2026-03-16 14:13:24 -0600",
132+
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2026-03-16 14:23:41 -0600",
133+
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2026-03-16 14:23:41 -0600",
134+
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2026-03-16 14:23:41 -0600",
135+
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2026-03-16 14:23:41 -0600",
136136
"tests/library_checker_aizu_tests/trees/hld_aizu1.test.cpp": "2026-01-18 11:15:41 +0000",
137137
"tests/library_checker_aizu_tests/trees/hld_aizu2.test.cpp": "2026-01-23 04:31:29 +0000",
138138
"tests/library_checker_aizu_tests/trees/hld_lib_checker_path.test.cpp": "2026-01-18 11:15:41 +0000",

library/trees/edge_cd.hpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,44 @@
55
//! https://youtu.be/wDwaMo5xa-k
66
//! @code
77
//! vector<basic_string<int>> adj(n);
8-
//! edge_cd(adj, [&](const auto& adj, int cent, int m) {
8+
//! edge_cd(adj, [&](int cent, int m) {
99
//! // subtrees of [0, m) of adj[cent]: 1st edge-set
1010
//! // subtrees of [m, sz(adj[cent])): 2nd edge-set
1111
//! });
1212
//! @endcode
1313
//! handle single-edge-paths separately
1414
//! @time O(n logφ n)
1515
//! @space O(n)
16-
template<class F, class G> struct edge_cd {
17-
vector<G> adj;
18-
F f;
19-
vi siz;
20-
edge_cd(const vector<G>& adj, F f):
21-
adj(adj), f(f), siz(sz(adj)) {
22-
dfs(0, sz(adj) - 1);
23-
}
24-
int find_cent(int u, int p, int m) {
16+
template<class G>
17+
void edge_cd(vector<G>& adj, const auto& f) {
18+
vi siz(sz(adj));
19+
auto find_cent = [&](auto&& self, int u, int p,
20+
int m) -> int {
2521
siz[u] = 1;
2622
for (int v : adj[u])
2723
if (v != p) {
28-
int cent = find_cent(v, u, m);
24+
int cent = self(self, v, u, m);
2925
if (cent != -1) return cent;
3026
siz[u] += siz[v];
3127
}
3228
return 2 * siz[u] > m
3329
? p >= 0 && (siz[p] = m + 1 - siz[u]),
3430
u : -1;
35-
}
36-
void dfs(int u, int m) {
31+
};
32+
auto dfs = [&](auto&& self, int u, int m) -> void {
3733
if (m < 2) return;
38-
u = find_cent(u, -1, m);
34+
u = find_cent(find_cent, u, -1, m);
3935
int sum = 0;
4036
auto it = partition(all(adj[u]), [&](int v) {
4137
ll x = sum + siz[v];
4238
return x * x < m * (m - x) ? sum += siz[v], 1 : 0;
4339
});
44-
f(adj, u, it - begin(adj[u]));
40+
f(u, it - begin(adj[u]));
4541
G oth(it, end(adj[u]));
4642
adj[u].erase(it, end(adj[u]));
47-
dfs(u, sum);
43+
self(self, u, sum);
4844
swap(adj[u], oth);
49-
dfs(u, m - sum);
50-
}
45+
self(self, u, m - sum);
46+
};
47+
dfs(dfs, 0, sz(adj) - 1);
5148
};

tests/library_checker_aizu_tests/edge_cd_asserts.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#pragma once
2-
void edge_cd_asserts(const vector<vi>& adj, int cent,
3-
int split) {
2+
auto edge_cd_asserts = [&](int cent, int split) -> void {
43
assert(0 < split && split < sz(adj[cent]));
54
auto dfs = [&](auto&& self, int u, int p) -> int {
65
int siz = 1;
@@ -47,4 +46,4 @@ void edge_cd_asserts(const vector<vi>& adj, int cent,
4746
assert(!is_balanced(a, cnts[0] + b));
4847
assert(!is_balanced(b, cnts[0] + a));
4948
}
50-
}
49+
};

tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,23 @@ vector<vector<ll>> naive(const vector<vi>& adj) {
9191
//! @time O(n * logφ(n) * log2(n))
9292
//! @space this function allocates/returns various vectors
9393
//! which are each O(n)
94-
vector<ll> count_paths_per_length(const vector<vi>& adj) {
94+
vector<ll> count_paths_per_length(vector<vi> adj) {
9595
vector<ll> num_paths(sz(adj));
9696
if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1;
97-
edge_cd(adj,
98-
[&](const vector<vi>& cd_adj, int cent, int split) {
99-
vector<vector<double>> cnt(2, vector<double>(1));
100-
auto dfs = [&](auto&& self, int u, int p, int d,
101-
int side) -> void {
102-
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
103-
cnt[side][d]++;
104-
for (int c : cd_adj[u])
105-
if (c != p) self(self, c, u, 1 + d, side);
106-
};
107-
rep(i, 0, sz(cd_adj[cent]))
108-
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
109-
vector<double> prod = conv(cnt[0], cnt[1]);
110-
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
111-
});
97+
edge_cd(adj, [&](int cent, int split) {
98+
vector<vector<double>> cnt(2, vector<double>(1));
99+
auto dfs = [&](auto&& self, int u, int p, int d,
100+
int side) -> void {
101+
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
102+
cnt[side][d]++;
103+
for (int c : adj[u])
104+
if (c != p) self(self, c, u, 1 + d, side);
105+
};
106+
rep(i, 0, sz(adj[cent]))
107+
dfs(dfs, adj[cent][i], cent, 1, i < split);
108+
vector<double> prod = conv(cnt[0], cnt[1]);
109+
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
110+
});
112111
return num_paths;
113112
}
114113
int main() {

tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
#define PROBLEM \
22
"https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
33
#include "../template.hpp"
4-
#include "../edge_cd_asserts.hpp"
54
#include "../../../kactl/stress-tests/utilities/genTree.h"
65
#include "../../../library/trees/edge_cd.hpp"
76
int main() {
87
{
98
vector<vector<int>> adj;
10-
edge_cd(adj,
11-
[&](const vector<vector<int>>&, int, int) -> void {
12-
assert(false);
13-
});
9+
edge_cd(adj, [&](int, int) -> void { assert(false); });
1410
}
1511
{
1612
vector<vector<int>> adj(1);
17-
edge_cd(adj,
18-
[&](const vector<vector<int>>&, int, int) -> void {
19-
assert(false);
20-
});
13+
edge_cd(adj, [&](int, int) -> void { assert(false); });
2114
}
2215
for (int n = 2; n <= 7; n++) {
2316
int num_codes = 1;
@@ -42,6 +35,7 @@ int main() {
4235
adj[u].push_back(v);
4336
adj[v].push_back(u);
4437
}
38+
#include "../edge_cd_asserts.hpp"
4539
edge_cd(adj, edge_cd_asserts);
4640
}
4741
}

tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,23 @@
99
//! @time O(n * logφ(n) * log2(n))
1010
//! @space this function allocates/returns various vectors
1111
//! which are each O(n)
12-
vector<ll> count_paths_per_length(const vector<vi>& adj) {
12+
vector<ll> count_paths_per_length(vector<vi>& adj) {
1313
vector<ll> num_paths(sz(adj));
1414
if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1;
15-
edge_cd(adj,
16-
[&](const vector<vi>& cd_adj, int cent, int split) {
17-
vector<vector<double>> cnt(2, vector<double>(1));
18-
auto dfs = [&](auto&& self, int u, int p, int d,
19-
int side) -> void {
20-
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
21-
cnt[side][d]++;
22-
for (int c : cd_adj[u])
23-
if (c != p) self(self, c, u, 1 + d, side);
24-
};
25-
rep(i, 0, sz(cd_adj[cent]))
26-
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
27-
vector<double> prod = conv(cnt[0], cnt[1]);
28-
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
29-
});
15+
edge_cd(adj, [&](int cent, int split) {
16+
vector<vector<double>> cnt(2, vector<double>(1));
17+
auto dfs = [&](auto&& self, int u, int p, int d,
18+
int side) -> void {
19+
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
20+
cnt[side][d]++;
21+
for (int c : adj[u])
22+
if (c != p) self(self, c, u, 1 + d, side);
23+
};
24+
rep(i, 0, sz(adj[cent]))
25+
dfs(dfs, adj[cent][i], cent, 1, i < split);
26+
vector<double> prod = conv(cnt[0], cnt[1]);
27+
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
28+
});
3029
return num_paths;
3130
}
3231
int main() {

tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#define PROBLEM \
22
"https://judge.yosupo.jp/problem/vertex_add_range_contour_sum_on_tree"
33
#include "../template.hpp"
4-
#include "../edge_cd_asserts.hpp"
54
#include "../../../library/data_structures_[l,r)/bit.hpp"
65
#include "../../../library/trees/edge_cd.hpp"
76
struct sum_adj {
@@ -47,25 +46,23 @@ struct contour_range_query {
4746
//! @param a a[u] = initial number for node u
4847
//! @time O(n logφ n)
4948
//! @space O(n logφ n) for `info` and `bits`
50-
contour_range_query(const vector<vi>& adj,
51-
const vector<ll>& a):
49+
contour_range_query(vector<vi> adj, const vector<ll>& a):
5250
n(sz(a)), sum_a(adj, a), info(n) {
53-
edge_cd(adj,
54-
[&](const vector<vi>& cd_adj, int cent, int split) {
55-
vector<vector<ll>> sum_num(2, vector<ll>(1));
56-
auto dfs = [&](auto&& self, int u, int p, int d,
57-
int side) -> void {
58-
info[u].push_back({int(sz(bits)), d, side});
59-
if (sz(sum_num[side]) == d)
60-
sum_num[side].push_back(0);
61-
sum_num[side][d] += a[u];
62-
for (int c : cd_adj[u])
63-
if (c != p) self(self, c, u, 1 + d, side);
64-
};
65-
rep(i, 0, sz(cd_adj[cent]))
66-
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
67-
bits.push_back({BIT(sum_num[0]), BIT(sum_num[1])});
68-
});
51+
edge_cd(adj, [&](int cent, int split) {
52+
vector<vector<ll>> sum_num(2, vector<ll>(1));
53+
auto dfs = [&](auto&& self, int u, int p, int d,
54+
int side) -> void {
55+
info[u].push_back({int(sz(bits)), d, side});
56+
if (sz(sum_num[side]) == d)
57+
sum_num[side].push_back(0);
58+
sum_num[side][d] += a[u];
59+
for (int c : adj[u])
60+
if (c != p) self(self, c, u, 1 + d, side);
61+
};
62+
rep(i, 0, sz(adj[cent]))
63+
dfs(dfs, adj[cent][i], cent, 1, i < split);
64+
bits.push_back({BIT(sum_num[0]), BIT(sum_num[1])});
65+
});
6966
}
7067
//! @param u node
7168
//! @param delta number to add to node u's number
@@ -108,8 +105,11 @@ int main() {
108105
adj[u].push_back(v);
109106
adj[v].push_back(u);
110107
}
111-
{ edge_cd(adj, edge_cd_asserts); }
112108
contour_range_query cq(adj, a);
109+
{
110+
#include "../edge_cd_asserts.hpp"
111+
edge_cd(adj, edge_cd_asserts);
112+
}
113113
while (q--) {
114114
int type;
115115
cin >> type;

tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#define PROBLEM \
22
"https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree"
33
#include "../template.hpp"
4-
#include "../edge_cd_asserts.hpp"
54
#include "../../../library/data_structures_[l,r)/bit_uncommon/rupq.hpp"
65
#include "../../../library/trees/edge_cd.hpp"
76
struct sum_adj {
@@ -49,24 +48,23 @@ struct contour_range_update {
4948
//! @param a a[u] = initial number for node u
5049
//! @time O(n logφ n)
5150
//! @space O(n logφ n) for `info` and `bits`
52-
contour_range_update(const vector<vi>& adj,
51+
contour_range_update(vector<vi> adj,
5352
const vector<ll>& a):
5453
n(sz(a)), a(a), sum_a(adj, vector<ll>(n)), info(n) {
55-
edge_cd(adj,
56-
[&](const vector<vi>& cd_adj, int cent, int split) {
57-
array<int, 2> mx_d = {0, 0};
58-
auto dfs = [&](auto&& self, int u, int p, int d,
59-
int side) -> void {
60-
mx_d[side] = max(mx_d[side], d);
61-
info[u].push_back({int(sz(bits)), d, side});
62-
for (int v : cd_adj[u])
63-
if (v != p) self(self, v, u, 1 + d, side);
64-
};
65-
rep(i, 0, sz(cd_adj[cent]))
66-
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
67-
bits.push_back(
68-
{bit_rupq(mx_d[0] + 1), bit_rupq(mx_d[1] + 1)});
69-
});
54+
edge_cd(adj, [&](int cent, int split) {
55+
array<int, 2> mx_d = {0, 0};
56+
auto dfs = [&](auto&& self, int u, int p, int d,
57+
int side) -> void {
58+
mx_d[side] = max(mx_d[side], d);
59+
info[u].push_back({int(sz(bits)), d, side});
60+
for (int v : adj[u])
61+
if (v != p) self(self, v, u, 1 + d, side);
62+
};
63+
rep(i, 0, sz(adj[cent]))
64+
dfs(dfs, adj[cent][i], cent, 1, i < split);
65+
bits.push_back(
66+
{bit_rupq(mx_d[0] + 1), bit_rupq(mx_d[1] + 1)});
67+
});
7068
}
7169
//! @param u,l,r,delta add delta to all nodes v such
7270
//! that l <= dist(u, v) < r
@@ -106,8 +104,11 @@ int main() {
106104
adj[u].push_back(v);
107105
adj[v].push_back(u);
108106
}
109-
{ edge_cd(adj, edge_cd_asserts); }
110107
contour_range_update cu(adj, a);
108+
{
109+
#include "../edge_cd_asserts.hpp"
110+
edge_cd(adj, edge_cd_asserts);
111+
}
111112
while (q--) {
112113
int type;
113114
cin >> type;

0 commit comments

Comments
 (0)