Skip to content

Commit a77bda3

Browse files
lrvideckisweb-flow
andauthored
More golf (#209)
* add back centroid test * fix * [auto-verifier] verify commit 31024fe * golf * fix * more golf * name nit --------- Co-authored-by: GitHub <noreply@github.com>
1 parent 0816ec2 commit a77bda3

7 files changed

Lines changed: 104 additions & 92 deletions

File tree

.verify-helper/timestamps.remote.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_aizu.test.cpp": "2026-04-06 14:41:55 -0600",
7575
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_lib_checker.test.cpp": "2026-04-06 14:41:55 -0600",
7676
"tests/library_checker_aizu_tests/graphs/two_edge_components.test.cpp": "2026-04-06 14:41:55 -0600",
77-
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-04-12 10:51:29 -0600",
77+
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-04-12 13:18:22 -0600",
7878
"tests/library_checker_aizu_tests/handmade_tests/dsu.test.cpp": "2026-01-22 10:08:22 -0700",
7979
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2026-04-12 10:51:29 -0600",
8080
"tests/library_checker_aizu_tests/handmade_tests/fib_matrix_expo.test.cpp": "2026-01-28 21:48:16 -0700",
@@ -131,7 +131,7 @@
131131
"tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp": "2026-04-12 06:15:25 -0600",
132132
"tests/library_checker_aizu_tests/strings/trie.test.cpp": "2026-01-17 12:38:18 -0700",
133133
"tests/library_checker_aizu_tests/strings/wildcard_pattern_matching.test.cpp": "2025-08-05 19:19:23 -0600",
134-
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2026-04-12 10:51:29 -0600",
134+
"tests/library_checker_aizu_tests/trees/cd_count_paths_per_length.test.cpp": "2026-04-12 13:19:58 -0600",
135135
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2026-04-12 10:51:29 -0600",
136136
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2026-04-12 10:51:29 -0600",
137137
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2026-04-12 10:51:29 -0600",

library/trees/centroid_decomp.hpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,19 @@
77
//! @space O(n)
88
void centroid(auto& g, auto f) {
99
vi siz(sz(g));
10-
auto dfs_sz = [&](auto&& dfs_sz, int u, int p) -> void {
10+
auto ctd = [&](auto&& ctd, int u, int p, int n) -> int {
1111
siz[u] = 1;
1212
for (int v : g[u])
13-
if (v != p) dfs_sz(dfs_sz, v, u), siz[u] += siz[v];
13+
if (v != p) {
14+
if (int c = ctd(ctd, v, u, n); c != -1) return c;
15+
siz[u] += siz[v];
16+
}
17+
return 2 * siz[u] >= n ? siz[p] = n - siz[u], u : -1;
1418
};
15-
auto dfs = [&](auto&& dfs, int u, int p) -> void {
16-
dfs_sz(dfs_sz, u, -1);
17-
for (int w = -1, sz_root = siz[u];;) {
18-
auto big_ch = ranges::find_if(g[u], [&](int v) {
19-
return v != w && 2 * siz[v] > sz_root;
20-
});
21-
if (big_ch == end(g[u])) break;
22-
w = u, u = *big_ch;
23-
}
24-
f(u, p);
25-
for (int v : g[u]) erase(g[v], u), dfs(dfs, v, u);
19+
auto dfs = [&](auto&& dfs, int u, int p, int n) -> void {
20+
f(u = ctd(ctd, u, u, n), p);
21+
for (int v : g[u])
22+
erase(g[v], u), dfs(dfs, v, u, siz[v]);
2623
};
27-
dfs(dfs, 0, -1);
24+
dfs(dfs, 0, -1, sz(g));
2825
}

library/trees/edge_cd.hpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,19 @@
1515
//! @space O(n)
1616
template<class G> void edge_cd(vector<G>& g, auto f) {
1717
vi siz(sz(g));
18-
auto cent = [&](auto&& cent, int u, int p,
19-
int m) -> int {
18+
auto ctd = [&](auto&& ctd, int u, int p, int m) -> int {
2019
siz[u] = 1;
2120
for (int v : g[u])
2221
if (v != p) {
23-
int c = cent(cent, v, u, m);
24-
if (c != -1) return c;
22+
if (int c = ctd(ctd, v, u, m); c != -1) return c;
2523
siz[u] += siz[v];
2624
}
2725
return 2 * siz[u] > m ? siz[p] = m + 1 - siz[u],
2826
u : -1;
2927
};
3028
auto dfs = [&](auto&& dfs, int u, int m) -> void {
3129
if (m < 2) return;
32-
u = cent(cent, u, u, m);
30+
u = ctd(ctd, u, u, m);
3331
int sum = 0;
3432
auto it = partition(all(g[u]), [&](int v) {
3533
ll x = sum + siz[v];
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
11
#pragma once
2+
#include "../../library/trees/centroid_decomp.hpp"
3+
void cd_asserts(vector<vector<int>> adj) {
4+
vector<int> decomp_size(sz(adj), -1);
5+
vector<int> naive_par_decomp(sz(adj), -1);
6+
centroid(adj, [&](int cent, int par_cent) -> void {
7+
assert(naive_par_decomp[cent] == par_cent);
8+
assert(decomp_size[cent] == -1);
9+
auto dfs = [&](auto&& self, int u, int p) -> int {
10+
naive_par_decomp[u] = cent;
11+
int sub_size = 1;
12+
for (int v : adj[u])
13+
if (v != p) sub_size += self(self, v, u);
14+
return sub_size;
15+
};
16+
decomp_size[cent] = dfs(dfs, cent, -1);
17+
if (par_cent != -1)
18+
assert(1 <= decomp_size[cent] &&
19+
2 * decomp_size[cent] <= decomp_size[par_cent]);
20+
for (int u : adj[cent]) {
21+
int sz_subtree = dfs(dfs, u, cent);
22+
assert(1 <= sz_subtree &&
23+
2 * sz_subtree <= decomp_size[cent]);
24+
}
25+
});
26+
rep(i, 0, sz(adj)) assert(decomp_size[i] >= 1);
27+
}

tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,7 @@
66
#include "../../../kactl/content/numerical/FastFourierTransform.h"
77
#include "../../../library/trees/edge_cd.hpp"
88
#include "../../../library/trees/centroid_decomp.hpp"
9-
void cd_asserts(vector<vector<int>> adj) {
10-
vector<int> decomp_size(sz(adj), -1);
11-
vector<int> naive_par_decomp(sz(adj), -1);
12-
centroid(adj, [&](int cent, int par_cent) -> void {
13-
assert(naive_par_decomp[cent] == par_cent);
14-
assert(decomp_size[cent] == -1);
15-
auto dfs = [&](auto&& self, int u, int p) -> int {
16-
naive_par_decomp[u] = cent;
17-
int sub_size = 1;
18-
for (int v : adj[u])
19-
if (v != p) sub_size += self(self, v, u);
20-
return sub_size;
21-
};
22-
decomp_size[cent] = dfs(dfs, cent, -1);
23-
if (par_cent != -1)
24-
assert(1 <= decomp_size[cent] &&
25-
2 * decomp_size[cent] <= decomp_size[par_cent]);
26-
for (int u : adj[cent]) {
27-
int sz_subtree = dfs(dfs, u, cent);
28-
assert(1 <= sz_subtree &&
29-
2 * sz_subtree <= decomp_size[cent]);
30-
}
31-
});
32-
rep(i, 0, sz(adj)) assert(decomp_size[i] >= 1);
33-
}
9+
#include "../cd_asserts.hpp"
3410
//! @param adj unrooted, connected forest
3511
//! @param k number of edges
3612
//! @returns array `num_paths` where `num_paths[i]` =
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#define PROBLEM \
2+
"https://judge.yosupo.jp/problem/frequency_table_of_tree_distance"
3+
#include "../template.hpp"
4+
#include "../cd_asserts.hpp"
5+
#include "../../../kactl/content/numerical/FastFourierTransform.h"
6+
//! @param adj unrooted, connected forest
7+
//! @returns array `num_paths` where `num_paths[i]` = # of
8+
//! paths in tree with `i` edges. `num_paths[1]` = # edges
9+
//! @time O(n log^2 n)
10+
//! @space this function allocates/returns various vectors
11+
//! which are each O(n)
12+
vector<ll> count_paths_per_length(vector<vi> adj) {
13+
vector<ll> num_paths(sz(adj));
14+
centroid(adj, [&](int cent, int) {
15+
vector<vector<double>> child_depths;
16+
for (int v : adj[cent]) {
17+
child_depths.emplace_back(1, 0.0);
18+
for (queue<pii> q({{v, cent}}); !empty(q);) {
19+
child_depths.back().push_back(sz(q));
20+
queue<pii> new_q;
21+
while (!empty(q)) {
22+
auto [u, p] = q.front();
23+
q.pop();
24+
for (int w : adj[u]) {
25+
if (w == p) continue;
26+
new_q.emplace(w, u);
27+
}
28+
}
29+
swap(q, new_q);
30+
}
31+
}
32+
sort(all(child_depths),
33+
[&](auto& x, auto& y) { return sz(x) < sz(y); });
34+
vector total_depth(1, 1.0);
35+
for (const auto& cnt_depth : child_depths) {
36+
auto prod = conv(total_depth, cnt_depth);
37+
rep(i, 1, sz(prod)) num_paths[i] += llround(prod[i]);
38+
total_depth.resize(sz(cnt_depth));
39+
rep(i, 1, sz(cnt_depth)) total_depth[i] +=
40+
cnt_depth[i];
41+
}
42+
});
43+
return num_paths;
44+
}
45+
int main() {
46+
cin.tie(0)->sync_with_stdio(0);
47+
int n;
48+
cin >> n;
49+
vector<vector<int>> adj(n);
50+
for (int i = 0; i < n - 1; i++) {
51+
int u, v;
52+
cin >> u >> v;
53+
adj[u].push_back(v);
54+
adj[v].push_back(u);
55+
}
56+
cd_asserts(adj);
57+
vector<ll> cnt_len = count_paths_per_length(adj);
58+
for (int i = 1; i < n; i++) cout << cnt_len[i] << " ";
59+
cout << '\n';
60+
return 0;
61+
}

tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)