Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .verify-helper/timestamps.remote.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_aizu.test.cpp": "2026-01-17 13:05:42 -0700",
"tests/library_checker_aizu_tests/graphs/strongly_connected_components_lib_checker.test.cpp": "2026-01-17 13:05:42 -0700",
"tests/library_checker_aizu_tests/graphs/two_edge_components.test.cpp": "2026-02-27 11:16:07 -0700",
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-03-16 14:02:06 -0600",
"tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/handmade_tests/dsu.test.cpp": "2026-01-22 10:08:22 -0700",
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/handmade_tests/fib_matrix_expo.test.cpp": "2026-01-28 21:48:16 -0700",
"tests/library_checker_aizu_tests/handmade_tests/functional_graph.test.cpp": "2025-08-06 16:18:37 -0600",
"tests/library_checker_aizu_tests/handmade_tests/hilbert_mos.test.cpp": "2026-01-18 02:20:40 +0000",
Expand Down Expand Up @@ -128,11 +128,11 @@
"tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp": "2026-01-28 01:00:19 -0700",
"tests/library_checker_aizu_tests/strings/trie.test.cpp": "2026-01-17 12:38:18 -0700",
"tests/library_checker_aizu_tests/strings/wildcard_pattern_matching.test.cpp": "2025-08-05 19:19:23 -0600",
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2026-03-16 13:39:47 -0600",
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2026-03-16 14:13:24 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2026-03-16 14:23:41 -0600",
"tests/library_checker_aizu_tests/trees/hld_aizu1.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/trees/hld_aizu2.test.cpp": "2026-01-23 04:31:29 +0000",
"tests/library_checker_aizu_tests/trees/hld_lib_checker_path.test.cpp": "2026-01-18 11:15:41 +0000",
Expand Down
33 changes: 15 additions & 18 deletions library/trees/edge_cd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,44 @@
//! https://youtu.be/wDwaMo5xa-k
//! @code
//! vector<basic_string<int>> adj(n);
//! edge_cd(adj, [&](const auto& adj, int cent, int m) {
//! edge_cd(adj, [&](int cent, int m) {
//! // subtrees of [0, m) of adj[cent]: 1st edge-set
//! // subtrees of [m, sz(adj[cent])): 2nd edge-set
//! });
//! @endcode
//! handle single-edge-paths separately
//! @time O(n logφ n)
//! @space O(n)
template<class F, class G> struct edge_cd {
vector<G> adj;
F f;
vi siz;
edge_cd(const vector<G>& adj, F f):
adj(adj), f(f), siz(sz(adj)) {
dfs(0, sz(adj) - 1);
}
int find_cent(int u, int p, int m) {
template<class G>
void edge_cd(vector<G>& adj, const auto& f) {
vi siz(sz(adj));
auto find_cent = [&](auto&& self, int u, int p,
int m) -> int {
siz[u] = 1;
for (int v : adj[u])
if (v != p) {
int cent = find_cent(v, u, m);
int cent = self(self, v, u, m);
if (cent != -1) return cent;
siz[u] += siz[v];
}
return 2 * siz[u] > m
? p >= 0 && (siz[p] = m + 1 - siz[u]),
u : -1;
}
void dfs(int u, int m) {
};
auto dfs = [&](auto&& self, int u, int m) -> void {
if (m < 2) return;
u = find_cent(u, -1, m);
u = find_cent(find_cent, u, -1, m);
int sum = 0;
auto it = partition(all(adj[u]), [&](int v) {
ll x = sum + siz[v];
return x * x < m * (m - x) ? sum += siz[v], 1 : 0;
});
f(adj, u, it - begin(adj[u]));
f(u, it - begin(adj[u]));
G oth(it, end(adj[u]));
adj[u].erase(it, end(adj[u]));
dfs(u, sum);
self(self, u, sum);
swap(adj[u], oth);
dfs(u, m - sum);
}
self(self, u, m - sum);
};
dfs(dfs, 0, sz(adj) - 1);
};
5 changes: 2 additions & 3 deletions tests/library_checker_aizu_tests/edge_cd_asserts.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once
void edge_cd_asserts(const vector<vi>& adj, int cent,
int split) {
auto edge_cd_asserts = [&](int cent, int split) -> void {
assert(0 < split && split < sz(adj[cent]));
auto dfs = [&](auto&& self, int u, int p) -> int {
int siz = 1;
Expand Down Expand Up @@ -47,4 +46,4 @@ void edge_cd_asserts(const vector<vi>& adj, int cent,
assert(!is_balanced(a, cnts[0] + b));
assert(!is_balanced(b, cnts[0] + a));
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,23 @@ vector<vector<ll>> naive(const vector<vi>& adj) {
//! @time O(n * logφ(n) * log2(n))
//! @space this function allocates/returns various vectors
//! which are each O(n)
vector<ll> count_paths_per_length(const vector<vi>& adj) {
vector<ll> count_paths_per_length(vector<vi> adj) {
vector<ll> num_paths(sz(adj));
if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1;
edge_cd(adj,
[&](const vector<vi>& cd_adj, int cent, int split) {
vector<vector<double>> cnt(2, vector<double>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
cnt[side][d]++;
for (int c : cd_adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(cd_adj[cent]))
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
vector<double> prod = conv(cnt[0], cnt[1]);
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
});
edge_cd(adj, [&](int cent, int split) {
vector<vector<double>> cnt(2, vector<double>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
cnt[side][d]++;
for (int c : adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(adj[cent]))
dfs(dfs, adj[cent][i], cent, 1, i < split);
vector<double> prod = conv(cnt[0], cnt[1]);
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
});
return num_paths;
}
int main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
#define PROBLEM \
"https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
#include "../template.hpp"
#include "../edge_cd_asserts.hpp"
#include "../../../kactl/stress-tests/utilities/genTree.h"
#include "../../../library/trees/edge_cd.hpp"
int main() {
{
vector<vector<int>> adj;
edge_cd(adj,
[&](const vector<vector<int>>&, int, int) -> void {
assert(false);
});
edge_cd(adj, [&](int, int) -> void { assert(false); });
}
{
vector<vector<int>> adj(1);
edge_cd(adj,
[&](const vector<vector<int>>&, int, int) -> void {
assert(false);
});
edge_cd(adj, [&](int, int) -> void { assert(false); });
}
for (int n = 2; n <= 7; n++) {
int num_codes = 1;
Expand All @@ -42,6 +35,7 @@ int main() {
adj[u].push_back(v);
adj[v].push_back(u);
}
#include "../edge_cd_asserts.hpp"
edge_cd(adj, edge_cd_asserts);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,23 @@
//! @time O(n * logφ(n) * log2(n))
//! @space this function allocates/returns various vectors
//! which are each O(n)
vector<ll> count_paths_per_length(const vector<vi>& adj) {
vector<ll> count_paths_per_length(vector<vi>& adj) {
vector<ll> num_paths(sz(adj));
if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1;
edge_cd(adj,
[&](const vector<vi>& cd_adj, int cent, int split) {
vector<vector<double>> cnt(2, vector<double>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
cnt[side][d]++;
for (int c : cd_adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(cd_adj[cent]))
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
vector<double> prod = conv(cnt[0], cnt[1]);
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
});
edge_cd(adj, [&](int cent, int split) {
vector<vector<double>> cnt(2, vector<double>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
if (sz(cnt[side]) == d) cnt[side].push_back(0.0);
cnt[side][d]++;
for (int c : adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(adj[cent]))
dfs(dfs, adj[cent][i], cent, 1, i < split);
vector<double> prod = conv(cnt[0], cnt[1]);
rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]);
});
return num_paths;
}
int main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#define PROBLEM \
"https://judge.yosupo.jp/problem/vertex_add_range_contour_sum_on_tree"
#include "../template.hpp"
#include "../edge_cd_asserts.hpp"
#include "../../../library/data_structures_[l,r)/bit.hpp"
#include "../../../library/trees/edge_cd.hpp"
struct sum_adj {
Expand Down Expand Up @@ -47,25 +46,23 @@ struct contour_range_query {
//! @param a a[u] = initial number for node u
//! @time O(n logφ n)
//! @space O(n logφ n) for `info` and `bits`
contour_range_query(const vector<vi>& adj,
const vector<ll>& a):
contour_range_query(vector<vi> adj, const vector<ll>& a):
n(sz(a)), sum_a(adj, a), info(n) {
edge_cd(adj,
[&](const vector<vi>& cd_adj, int cent, int split) {
vector<vector<ll>> sum_num(2, vector<ll>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
info[u].push_back({int(sz(bits)), d, side});
if (sz(sum_num[side]) == d)
sum_num[side].push_back(0);
sum_num[side][d] += a[u];
for (int c : cd_adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(cd_adj[cent]))
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
bits.push_back({BIT(sum_num[0]), BIT(sum_num[1])});
});
edge_cd(adj, [&](int cent, int split) {
vector<vector<ll>> sum_num(2, vector<ll>(1));
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
info[u].push_back({int(sz(bits)), d, side});
if (sz(sum_num[side]) == d)
sum_num[side].push_back(0);
sum_num[side][d] += a[u];
for (int c : adj[u])
if (c != p) self(self, c, u, 1 + d, side);
};
rep(i, 0, sz(adj[cent]))
dfs(dfs, adj[cent][i], cent, 1, i < split);
bits.push_back({BIT(sum_num[0]), BIT(sum_num[1])});
});
}
//! @param u node
//! @param delta number to add to node u's number
Expand Down Expand Up @@ -108,8 +105,11 @@ int main() {
adj[u].push_back(v);
adj[v].push_back(u);
}
{ edge_cd(adj, edge_cd_asserts); }
contour_range_query cq(adj, a);
{
#include "../edge_cd_asserts.hpp"
edge_cd(adj, edge_cd_asserts);
}
while (q--) {
int type;
cin >> type;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#define PROBLEM \
"https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree"
#include "../template.hpp"
#include "../edge_cd_asserts.hpp"
#include "../../../library/data_structures_[l,r)/bit_uncommon/rupq.hpp"
#include "../../../library/trees/edge_cd.hpp"
struct sum_adj {
Expand Down Expand Up @@ -49,24 +48,23 @@ struct contour_range_update {
//! @param a a[u] = initial number for node u
//! @time O(n logφ n)
//! @space O(n logφ n) for `info` and `bits`
contour_range_update(const vector<vi>& adj,
contour_range_update(vector<vi> adj,
const vector<ll>& a):
n(sz(a)), a(a), sum_a(adj, vector<ll>(n)), info(n) {
edge_cd(adj,
[&](const vector<vi>& cd_adj, int cent, int split) {
array<int, 2> mx_d = {0, 0};
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
mx_d[side] = max(mx_d[side], d);
info[u].push_back({int(sz(bits)), d, side});
for (int v : cd_adj[u])
if (v != p) self(self, v, u, 1 + d, side);
};
rep(i, 0, sz(cd_adj[cent]))
dfs(dfs, cd_adj[cent][i], cent, 1, i < split);
bits.push_back(
{bit_rupq(mx_d[0] + 1), bit_rupq(mx_d[1] + 1)});
});
edge_cd(adj, [&](int cent, int split) {
array<int, 2> mx_d = {0, 0};
auto dfs = [&](auto&& self, int u, int p, int d,
int side) -> void {
mx_d[side] = max(mx_d[side], d);
info[u].push_back({int(sz(bits)), d, side});
for (int v : adj[u])
if (v != p) self(self, v, u, 1 + d, side);
};
rep(i, 0, sz(adj[cent]))
dfs(dfs, adj[cent][i], cent, 1, i < split);
bits.push_back(
{bit_rupq(mx_d[0] + 1), bit_rupq(mx_d[1] + 1)});
});
}
//! @param u,l,r,delta add delta to all nodes v such
//! that l <= dist(u, v) < r
Expand Down Expand Up @@ -106,8 +104,11 @@ int main() {
adj[u].push_back(v);
adj[v].push_back(u);
}
{ edge_cd(adj, edge_cd_asserts); }
contour_range_update cu(adj, a);
{
#include "../edge_cd_asserts.hpp"
edge_cd(adj, edge_cd_asserts);
}
while (q--) {
int type;
cin >> type;
Expand Down
Loading
Loading