Skip to content

Commit 94fdc40

Browse files
lrvideckisweb-flow
andauthored
Edge cd upd (#131)
* adding progress * [auto-verifier] verify commit 5072068 * updates * Delete library/trees/edge_centroid_decomp_uncommon/README.md * format * fix * update complexity --------- Co-authored-by: GitHub <noreply@github.com>
1 parent 4fe5717 commit 94fdc40

File tree

10 files changed

+90
-101
lines changed

10 files changed

+90
-101
lines changed

.verify-helper/timestamps.remote.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"tests/library_checker_aizu_tests/graphs/two_edge_components.test.cpp": "2025-02-10 23:30:47 -0700",
6666
"tests/library_checker_aizu_tests/handmade_tests/count_paths_forest.test.cpp": "2024-12-15 14:34:10 -0600",
6767
"tests/library_checker_aizu_tests/handmade_tests/dsu_size.test.cpp": "2024-12-14 19:50:29 -0600",
68-
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2024-12-15 17:01:11 -0600",
68+
"tests/library_checker_aizu_tests/handmade_tests/edge_cd_small_trees.test.cpp": "2025-04-22 19:30:56 -0600",
6969
"tests/library_checker_aizu_tests/handmade_tests/fib_matrix_expo.test.cpp": "2024-12-14 19:50:29 -0600",
7070
"tests/library_checker_aizu_tests/handmade_tests/functional_graph.test.cpp": "2025-02-10 23:30:47 -0700",
7171
"tests/library_checker_aizu_tests/handmade_tests/lca_ladder_forest.test.cpp": "2025-02-10 23:30:47 -0700",
@@ -124,10 +124,10 @@
124124
"tests/library_checker_aizu_tests/strings/trie.test.cpp": "2024-12-05 10:41:42 -0600",
125125
"tests/library_checker_aizu_tests/strings/wildcard_pattern_matching.test.cpp": "2024-12-14 19:50:29 -0600",
126126
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2024-12-15 14:34:10 -0600",
127-
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2024-12-15 17:01:11 -0600",
128-
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2024-12-15 17:01:11 -0600",
129-
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2024-12-15 17:01:11 -0600",
130-
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2024-12-15 17:01:11 -0600",
127+
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp": "2025-04-22 19:30:56 -0600",
128+
"tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp": "2025-04-22 19:30:56 -0600",
129+
"tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp": "2025-04-22 19:30:56 -0600",
130+
"tests/library_checker_aizu_tests/trees/edge_cd_reroot_dp.test.cpp": "2025-04-22 19:30:56 -0600",
131131
"tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp": "2025-02-10 23:30:47 -0700",
132132
"tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp": "2025-02-10 23:30:47 -0700",
133133
"tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp": "2025-02-10 14:50:36 -0700",

library/trees/edge_cd.hpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,41 @@
1919
//! });
2020
//! @endcode
2121
//! handle single-edge-paths separately
22-
//! @time O(n log1.5 n)
22+
//! @time O(n logφ n)
2323
//! @space O(n)
2424
template<class F, class G> struct edge_cd {
2525
vector<G> adj;
2626
F f;
27-
vi sub_sz;
27+
vi siz;
2828
edge_cd(const vector<G>& adj, F f):
29-
adj(adj), f(f), sub_sz(sz(adj)) {
30-
dfs(0, sz(adj));
29+
adj(adj), f(f), siz(sz(adj)) {
30+
dfs(0, sz(adj) - 1);
3131
}
32-
int find_cent(int v, int p, int siz) {
33-
sub_sz[v] = 1;
32+
int find_cent(int v, int p, int m) {
33+
siz[v] = 1;
3434
for (int u : adj[v])
3535
if (u != p) {
36-
int cent = find_cent(u, v, siz);
36+
int cent = find_cent(u, v, m);
3737
if (cent != -1) return cent;
38-
sub_sz[v] += sub_sz[u];
38+
siz[v] += siz[u];
3939
}
4040
if (p == -1) return v;
41-
return 2 * sub_sz[v] >= siz
42-
? sub_sz[p] = siz - sub_sz[v],
43-
v : -1;
41+
return 2 * siz[v] > m ? siz[p] = m + 1 - siz[v],
42+
v : -1;
4443
}
45-
void dfs(int v, int siz) {
46-
if (siz <= 2) return;
47-
v = find_cent(v, -1, siz);
44+
void dfs(int v, int m) {
45+
if (m < 2) return;
46+
v = find_cent(v, -1, m);
4847
int sum = 0;
4948
auto it = partition(all(adj[v]), [&](int u) {
50-
bool ret = 2 * sum + sub_sz[u] < siz - 1 &&
51-
3 * (sum + sub_sz[u]) <= 2 * (siz - 1);
52-
if (ret) sum += sub_sz[u];
53-
return ret;
49+
ll x = sum + siz[u];
50+
return x * x < m * (m - x) ? sum += siz[u], 1 : 0;
5451
});
5552
f(adj, v, it - begin(adj[v]));
5653
G oth(it, end(adj[v]));
5754
adj[v].erase(it, end(adj[v]));
58-
dfs(v, sum + 1);
55+
dfs(v, sum);
5956
swap(adj[v], oth);
60-
dfs(v, siz - sum);
57+
dfs(v, m - sum);
6158
}
6259
};

library/trees/edge_centroid_decomp_uncommon/README.md

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

library/trees/edge_centroid_decomp_uncommon/contour_range_query.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22
#include "../../data_structures/bit.hpp"
3-
#include "sum_adjacent.hpp"
43
#include "../edge_cd.hpp"
4+
#include "sum_adjacent.hpp"
55
//! https://judge.yosupo.jp/problem/vertex_add_range_contour_sum_on_tree
66
struct contour_range_query {
77
int n;
@@ -10,8 +10,8 @@ struct contour_range_query {
1010
vector<array<BIT, 2>> bits;
1111
//! @param adj unrooted, undirected tree
1212
//! @param a a[v] = initial number for node v
13-
//! @time O(n log1.5 n)
14-
//! @space O(n log1.5 n) for `info` and `bits`
13+
//! @time O(n logφ n)
14+
//! @space O(n logφ n) for `info` and `bits`
1515
contour_range_query(const vector<vi>& adj,
1616
const vector<ll>& a):
1717
n(sz(a)), sum_a(adj, a), info(n) {
@@ -34,7 +34,7 @@ struct contour_range_query {
3434
}
3535
//! @param v node
3636
//! @param delta number to add to node v's number
37-
//! @time O(log1.5(n) * log2(n))
37+
//! @time O(logφ(n) * log2(n))
3838
//! @space O(1)
3939
void update(int v, ll delta) {
4040
sum_a.update(v, delta);
@@ -46,7 +46,7 @@ struct contour_range_query {
4646
//! @returns sum of node u's number over all u such that
4747
//! l
4848
//! <= dist_edges(u, v) < r
49-
//! @time O(log1.5(n) * log2(n))
49+
//! @time O(logφ(n) * log2(n))
5050
//! @space O(1)
5151
ll query(int v, int l, int r) {
5252
ll sum = 0;

library/trees/edge_centroid_decomp_uncommon/contour_range_update.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22
#include "../../data_structures/bit_uncommon/rupq.hpp"
3-
#include "sum_adjacent.hpp"
43
#include "../edge_cd.hpp"
4+
#include "sum_adjacent.hpp"
55
//! https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree
66
struct contour_range_update {
77
int n;
@@ -11,8 +11,8 @@ struct contour_range_update {
1111
vector<array<bit_rupq, 2>> bits;
1212
//! @param adj unrooted, undirected tree
1313
//! @param a a[v] = initial number for node v
14-
//! @time O(n log1.5 n)
15-
//! @space O(n log1.5 n) for `info` and `bits`
14+
//! @time O(n logφ n)
15+
//! @space O(n logφ n) for `info` and `bits`
1616
contour_range_update(const vector<vi>& adj,
1717
const vector<ll>& a):
1818
n(sz(a)), a(a), sum_a(adj, vector<ll>(n)), info(n) {
@@ -34,7 +34,7 @@ struct contour_range_update {
3434
}
3535
//! @param v,l,r,delta add delta to all nodes u such
3636
//! that l <= dist_edges(v, u) < r
37-
//! @time O(log1.5(n) * log2(n))
37+
//! @time O(logφ(n) * log2(n))
3838
//! @space O(1)
3939
void update(int v, int l, int r, ll delta) {
4040
if (l <= 0 && 0 < r) a[v] += delta;
@@ -48,7 +48,7 @@ struct contour_range_update {
4848
}
4949
//! @param v node
5050
//! @returns number of node v
51-
//! @time O(log1.5(n) * log2(n))
51+
//! @time O(logφ(n) * log2(n))
5252
//! @space O(1)
5353
ll query(int v) {
5454
ll sum = a[v] + sum_a.query(v);

library/trees/edge_centroid_decomp_uncommon/count_paths_per_length.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! @param adj unrooted, connected tree
55
//! @returns array `num_paths` where `num_paths[i]` = # of
66
//! paths in tree with `i` edges. `num_paths[1]` = # edges
7-
//! @time O(n * log1.5(n) * log2(n))
7+
//! @time O(n * logφ(n) * log2(n))
88
//! @space this function allocates/returns various vectors
99
//! which are each O(n)
1010
vector<ll> count_paths_per_length(const vector<vi>& adj) {

tests/library_checker_aizu_tests/edge_cd_asserts.hpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,36 @@ void edge_cd_asserts(const vector<vi>& adj, int cent,
1111
int sz_all = dfs(dfs, cent, -1);
1212
assert(sz_all >= 3);
1313
array<int, 2> cnts = {0, 0};
14+
array<int, 2> max_cnt = {0, 0};
1415
for (int i = 0; i < sz(adj[cent]); i++) {
1516
int sz_subtree = dfs(dfs, adj[cent][i], cent);
1617
assert(2 * sz_subtree <= sz_all);
1718
cnts[i < split] += sz_subtree;
19+
max_cnt[i < split] =
20+
max(max_cnt[i < split], sz_subtree);
1821
}
1922
assert(cnts[0] + cnts[1] + 1 == sz_all);
20-
for (int i = 0; i < 2; i++)
21-
assert(0 < cnts[i] && cnts[i] <= 2 * cnts[!i]);
23+
if (sz_all == 4) return;
24+
// a is the number of edges in the smaller edge set
25+
// b is the number of edges in the larger edge set
26+
// so we know 1/2 <= b/(a+b)
27+
// returns true iff b/(a+b) <= 1/phi
28+
auto is_balanced = [&](ll a, ll b) -> bool {
29+
assert(a <= b);
30+
return b * b <= a * (a + b);
31+
};
32+
if (cnts[0] > cnts[1]) {
33+
swap(cnts[0], cnts[1]);
34+
swap(max_cnt[0], max_cnt[1]);
35+
}
36+
if (!is_balanced(cnts[0], cnts[1])) {
37+
int a = max_cnt[1];
38+
int b = cnts[1] - max_cnt[1];
39+
assert(a > 0);
40+
assert(b > 0);
41+
if (a > b) swap(a, b);
42+
assert(is_balanced(a, b));
43+
assert(!is_balanced(a, cnts[0] + b));
44+
assert(!is_balanced(b, cnts[0] + a));
45+
}
2246
}

tests/library_checker_aizu_tests/handmade_tests/functional_graph.test.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/ITP1_1_A"
1+
#define PROBLEM \
2+
"https://onlinejudge.u-aizu.ac.jp/problems/ITP1_1_A"
23
#include "../template.hpp"
34
#include "../../../library/contest/random.hpp"
45
#include "../../../library/graphs/functional_graph_processor.hpp"
@@ -8,7 +9,7 @@ struct functional_graph_processor {
89
init(sz(next));
910
build(next);
1011
}
11-
template <class Graph_t>
12+
template<class Graph_t>
1213
functional_graph_processor(const Graph_t &g) {
1314
init(g.n);
1415
build(g);
@@ -100,31 +101,30 @@ struct functional_graph_processor {
100101
}
101102
int n;
102103
vector<vector<int>> cycle;
103-
vector<int> cycle_id; // id of the cycle it belongs to,
104-
// -1 if not part of one
105-
vector<int> cycle_pos; // position in its cycle, -1 if
106-
// not part of one
107-
vector<int> cycle_prev; // previous vertex in its cycle,
108-
// -1 if not part of one
109-
vector<int> component_size; // size of its weakly
110-
// connected component
111-
vector<int> root_of; // first reachable node in a cycle
112-
vector<int> depth; // distance to its root
113-
vector<vector<int>> abr; // forest of arborescences of reversed edges not
114-
// on the cycles
115-
vector<int> order; // dfs order of abr
116-
vector<int> pos; // pos in the dfs order
117-
vector<int> end; // [pos[u], end[u]) denotes the subtree
118-
vector<int> size; // size of the subtree in abr
104+
vector<int> cycle_id; // id of the cycle it belongs to,
105+
// -1 if not part of one
106+
vector<int> cycle_pos; // position in its cycle, -1 if
107+
// not part of one
108+
vector<int> cycle_prev; // previous vertex in its cycle,
109+
// -1 if not part of one
110+
vector<int> component_size; // size of its weakly
111+
// connected component
112+
vector<int> root_of; // first reachable node in a cycle
113+
vector<int> depth; // distance to its root
114+
vector<vector<int>>
115+
abr; // forest of arborescences of reversed edges not
116+
// on the cycles
117+
vector<int> order; // dfs order of abr
118+
vector<int> pos; // pos in the dfs order
119+
vector<int> end; // [pos[u], end[u]) denotes the subtree
120+
vector<int> size; // size of the subtree in abr
119121
};
120-
121122
bool equal(const basic_string<int> &a, const vi &b) {
122123
if (sz(a) != sz(b)) return 0;
123124
for (int i = 0; i < sz(a); i++)
124125
if (a[i] != b[i]) return 0;
125126
return 1;
126127
}
127-
128128
int main() {
129129
cin.tie(0)->sync_with_stdio(0);
130130
for (int num_tests = 100; num_tests--;) {
@@ -135,19 +135,23 @@ int main() {
135135
functional_graph_processor fgp(a);
136136
assert(cycle == fgp.cycle);
137137
for (int i = 0; i < n; i++) {
138-
int root = cycle[t[i].root_of.first][t[i].root_of.second];
138+
int root =
139+
cycle[t[i].root_of.first][t[i].root_of.second];
139140
assert(root == fgp.root_of[i]);
140141
assert(equal(t[i].childs, fgp.abr[i]));
141142
assert((root == i) == (fgp.cycle_id[i] != -1));
142143
if (root == i) {
143144
assert(t[i].root_of.first == fgp.cycle_id[i]);
144145
assert(t[i].root_of.second == fgp.cycle_pos[i]);
145146
int cyc_len = ssize(cycle[t[i].root_of.first]);
146-
assert(cycle[t[i].root_of.first][(t[i].root_of.second + 1) % cyc_len] ==
147-
a[i]);
147+
assert(
148+
cycle[t[i].root_of.first]
149+
[(t[i].root_of.second + 1) % cyc_len] ==
150+
a[i]);
148151
assert(fgp.cycle_prev[i] ==
149-
cycle[t[i].root_of.first]
150-
[(t[i].root_of.second - 1 + cyc_len) % cyc_len]);
152+
cycle[t[i].root_of.first]
153+
[(t[i].root_of.second - 1 + cyc_len) %
154+
cyc_len]);
151155
} else {
152156
assert(fgp.cycle_prev[i] == -1);
153157
}

tests/library_checker_aizu_tests/math/xor_basis.test.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ int main() {
2626
int64_t val2 = unordered.shrink(val1);
2727
assert(val1 == val2);
2828
for (int64_t v : unordered.b)
29-
assert((bit_floor((unsigned long long)v) & val2) == 0);
29+
assert(
30+
(bit_floor((unsigned long long)v) & val2) == 0);
3031
bool inserted_unordered = unordered.insert(elem);
3132
bool inserted_ordered_ll = ordered_ll.insert(elem);
3233
bool inserted_ordered_bitset =

tests/scripts/ptc.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ rm ../library/monotonic_stack/mono_stack_ri.png || exit 1
3030
rm ../library/strings/manacher/longest_palindrome_query.hpp || exit 1
3131
rm ../library/trees/centroid_decomp_uncommon/count_paths_per_length.hpp || exit 1
3232
rm ../library/trees/centroid_decomp_uncommon/count_paths_per_node.hpp || exit 1
33-
rm ../library/trees/edge_centroid_decomp_uncommon/README.md || exit 1
3433
rm ../library/trees/edge_centroid_decomp_uncommon/contour_range_query.hpp || exit 1
3534
rm ../library/trees/edge_centroid_decomp_uncommon/contour_range_update.hpp || exit 1
3635
rm ../library/trees/edge_centroid_decomp_uncommon/count_paths_per_length.hpp || exit 1

0 commit comments

Comments
 (0)