diff --git a/library/trees/uncommon/centroid_decomp.hpp b/library/trees/centroid_decomp.hpp similarity index 100% rename from library/trees/uncommon/centroid_decomp.hpp rename to library/trees/centroid_decomp.hpp diff --git a/library/trees/uncommon/contour_range_query.hpp b/library/trees/uncommon/contour_range_query.hpp deleted file mode 100644 index a0f5e7472..000000000 --- a/library/trees/uncommon/contour_range_query.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include "../../data_structures_[l,r)/bit.hpp" -#include "../edge_cd.hpp" -#include "sum_adjacent.hpp" -//! https://judge.yosupo.jp/problem/vertex_add_range_contour_sum_on_tree -struct contour_range_query { - int n; - sum_adj sum_a; - vector>> info; - vector> bits; - //! @param adj unrooted, undirected tree - //! @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& adj, - const vector& a): - n(sz(a)), sum_a(adj, a), info(n) { - edge_cd(adj, - [&](const vector& cd_adj, int cent, int split) { - vector> sum_num(2, vector(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])}); - }); - } - //! @param u node - //! @param delta number to add to node u's number - //! @time O(logφ(n) * log2(n)) - //! @space O(1) - void update(int u, ll delta) { - sum_a.update(u, delta); - for (auto [decomp, d, side] : info[u]) - bits[decomp][side].update(d, delta); - } - //! @param u node - //! @param l,r defines range [l, r) - //! @returns sum of node v's number over all v such that - //! l <= dist(u, v) < r - //! @time O(logφ(n) * log2(n)) - //! @space O(1) - ll query(int u, int l, int r) { - ll sum = 0; - if (l <= 0 && 0 < r) sum += sum_a.sum[u]; - if (l <= 1 && 1 < r) sum += sum_a.query(u); - for (auto [decomp, d, side] : info[u]) { - auto& bit = bits[decomp][!side]; - int my_l = clamp(l - d, 1, sz(bit.s)); - int my_r = clamp(r - d, 1, sz(bit.s)); - sum += bit.query(my_l, my_r); - } - return sum; - } -}; diff --git a/library/trees/uncommon/contour_range_update.hpp b/library/trees/uncommon/contour_range_update.hpp deleted file mode 100644 index b19a4be17..000000000 --- a/library/trees/uncommon/contour_range_update.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include "../../data_structures_[l,r)/bit_uncommon/rupq.hpp" -#include "../edge_cd.hpp" -#include "sum_adjacent.hpp" -//! https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree -struct contour_range_update { - int n; - vector a; - sum_adj sum_a; - vector>> info; - vector> bits; - //! @param adj unrooted, undirected tree - //! @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& adj, - const vector& a): - n(sz(a)), a(a), sum_a(adj, vector(n)), info(n) { - edge_cd(adj, - [&](const vector& cd_adj, int cent, int split) { - array 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)}); - }); - } - //! @param u,l,r,delta add delta to all nodes v such - //! that l <= dist(u, v) < r - //! @time O(logφ(n) * log2(n)) - //! @space O(1) - void update(int u, int l, int r, ll delta) { - if (l <= 0 && 0 < r) a[u] += delta; - if (l <= 1 && 1 < r) sum_a.update(u, delta); - for (auto [decomp_id, d, side] : info[u]) { - auto& bit = bits[decomp_id][!side]; - int my_l = clamp(l - d, 1, bit.n); - int my_r = clamp(r - d, 1, bit.n); - bit.update(my_l, my_r, delta); - } - } - //! @param u node - //! @returns number of node u - //! @time O(logφ(n) * log2(n)) - //! @space O(1) - ll query(int u) { - ll sum = a[u] + sum_a.query(u); - for (auto [decomp_id, d, side] : info[u]) - sum += bits[decomp_id][side].get_index(d); - return sum; - } -}; diff --git a/library/trees/uncommon/count_paths_per_length.hpp b/library/trees/uncommon/count_paths_per_length.hpp deleted file mode 100644 index f1aec47a9..000000000 --- a/library/trees/uncommon/count_paths_per_length.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "../../../kactl/content/numerical/FastFourierTransform.h" -#include "../edge_cd.hpp" -//! @param adj unrooted, connected tree -//! @returns array `num_paths` where `num_paths[i]` = # of -//! paths in tree with `i` edges. `num_paths[1]` = # edges -//! @time O(n * logφ(n) * log2(n)) -//! @space this function allocates/returns various vectors -//! which are each O(n) -vector count_paths_per_length(const vector& adj) { - vector num_paths(sz(adj)); - if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1; - edge_cd(adj, - [&](const vector& cd_adj, int cent, int split) { - vector> cnt(2, vector(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 prod = conv(cnt[0], cnt[1]); - rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]); - }); - return num_paths; -} diff --git a/library/trees/uncommon/count_paths_per_node.hpp b/library/trees/uncommon/count_paths_per_node.hpp deleted file mode 100644 index 21f7db17a..000000000 --- a/library/trees/uncommon/count_paths_per_node.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "centroid_decomp.hpp" -//! @param adj unrooted, connected forest -//! @param k number of edges -//! @returns array `num_paths` where `num_paths[i]` = -//! number of paths with k edges where node `i` is on the -//! path. 0-based nodes. -//! @time O(n log n) -//! @space this function allocates/returns various vectors -//! which are all O(n) -vector count_paths_per_node(const vector& adj, - int k) { - vector num_paths(sz(adj)); - centroid(adj, - [&](const vector& cd_adj, int cent, int) { - vector pre_d{1}, cur_d{0}; - auto dfs = [&](auto&& self, int u, int p, - int d) -> ll { - if (d > k) return 0LL; - if (sz(cur_d) <= d) cur_d.push_back(0); - cur_d[d]++; - ll cnt = 0; - if (k - d < sz(pre_d)) cnt += pre_d[k - d]; - for (int c : cd_adj[u]) - if (c != p) cnt += self(self, c, u, d + 1); - num_paths[u] += cnt; - return cnt; - }; - auto dfs_child = [&](int child) -> ll { - ll cnt = dfs(dfs, child, cent, 1); - pre_d.resize(sz(cur_d)); - for (int i = 1; i < sz(cur_d) && cur_d[i]; i++) - pre_d[i] += cur_d[i], cur_d[i] = 0; - return cnt; - }; - for (int child : cd_adj[cent]) - num_paths[cent] += dfs_child(child); - pre_d = cur_d = {0}; - for (int child : cd_adj[cent] | views::reverse) - dfs_child(child); - }); - return num_paths; -} diff --git a/library/trees/linear_kth_par.hpp b/library/trees/uncommon/linear_kth_par.hpp similarity index 100% rename from library/trees/linear_kth_par.hpp rename to library/trees/uncommon/linear_kth_par.hpp diff --git a/library/trees/uncommon/sum_adjacent.hpp b/library/trees/uncommon/sum_adjacent.hpp deleted file mode 100644 index 623e5562c..000000000 --- a/library/trees/uncommon/sum_adjacent.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -//! helper for edge CD: given tree, update node's number, -//! find sum of neighbors' numbers -struct sum_adj { - int n; - vector sum, sum_ch; - vi p; - //! @param adj undirected, unrooted tree - //! @param sum sum[u] = initial number for node u - //! @time O(n) - //! @space various O(n) vectors are allocated; recursion - //! stack for dfs is O(n) - sum_adj(const vector& adj, const vector& sum): - n(sz(sum)), sum(sum), sum_ch(n), p(n, -1) { - auto dfs = [&](auto&& self, int u) -> void { - for (int c : adj[u]) - if (c != p[u]) - p[c] = u, sum_ch[u] += sum[c], self(self, c); - }; - dfs(dfs, 0); - } - //! @param u node - //! @param delta number to add - //! @time O(1) - //! @space O(1) - void update(int u, ll delta) { - sum[u] += delta; - if (p[u] != -1) sum_ch[p[u]] += delta; - } - //! @param u node - //! @returns sum of u's neighbors numbers - //! @time O(1) - //! @space O(1) - ll query(int u) { - return sum_ch[u] + (p[u] != -1 ? sum[p[u]] : 0); - } -}; diff --git a/tests/library_checker_aizu_tests/cd_asserts.hpp b/tests/library_checker_aizu_tests/cd_asserts.hpp index db3ecdddd..8dcb6820d 100644 --- a/tests/library_checker_aizu_tests/cd_asserts.hpp +++ b/tests/library_checker_aizu_tests/cd_asserts.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../../library/trees/uncommon/centroid_decomp.hpp" +#include "../../library/trees/centroid_decomp.hpp" void cd_asserts(const vector>& adj) { vector decomp_size(sz(adj), -1); vector naive_par_decomp(sz(adj), -1); diff --git a/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp b/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp index 73c058b65..ef00a8452 100644 --- a/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp +++ b/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp @@ -2,10 +2,51 @@ "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A" #include "../template.hpp" #include "../../../library/contest/random.hpp" -#include "../../../library/trees/uncommon/count_paths_per_node.hpp" #include "../../../library/trees/lca_rmq.hpp" -#include "../../../library/trees/uncommon/count_paths_per_length.hpp" #include "../cd_asserts.hpp" +#include "../../../kactl/content/numerical/FastFourierTransform.h" +#include "../../../library/trees/edge_cd.hpp" +//! @param adj unrooted, connected forest +//! @param k number of edges +//! @returns array `num_paths` where `num_paths[i]` = +//! number of paths with k edges where node `i` is on the +//! path. 0-based nodes. +//! @time O(n log n) +//! @space this function allocates/returns various vectors +//! which are all O(n) +vector count_paths_per_node(const vector& adj, + int k) { + vector num_paths(sz(adj)); + centroid(adj, + [&](const vector& cd_adj, int cent, int) { + vector pre_d{1}, cur_d{0}; + auto dfs = [&](auto&& self, int u, int p, + int d) -> ll { + if (d > k) return 0LL; + if (sz(cur_d) <= d) cur_d.push_back(0); + cur_d[d]++; + ll cnt = 0; + if (k - d < sz(pre_d)) cnt += pre_d[k - d]; + for (int c : cd_adj[u]) + if (c != p) cnt += self(self, c, u, d + 1); + num_paths[u] += cnt; + return cnt; + }; + auto dfs_child = [&](int child) -> ll { + ll cnt = dfs(dfs, child, cent, 1); + pre_d.resize(sz(cur_d)); + for (int i = 1; i < sz(cur_d) && cur_d[i]; i++) + pre_d[i] += cur_d[i], cur_d[i] = 0; + return cnt; + }; + for (int child : cd_adj[cent]) + num_paths[cent] += dfs_child(child); + pre_d = cur_d = {0}; + for (int child : cd_adj[cent] | views::reverse) + dfs_child(child); + }); + return num_paths; +} vector> naive(const vector& adj) { LCA lc(adj); int n = sz(adj); @@ -21,6 +62,32 @@ vector> naive(const vector& adj) { } return cnts_naive; } +//! @param adj unrooted, connected tree +//! @returns array `num_paths` where `num_paths[i]` = # of +//! paths in tree with `i` edges. `num_paths[1]` = # edges +//! @time O(n * logφ(n) * log2(n)) +//! @space this function allocates/returns various vectors +//! which are each O(n) +vector count_paths_per_length(const vector& adj) { + vector num_paths(sz(adj)); + if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1; + edge_cd(adj, + [&](const vector& cd_adj, int cent, int split) { + vector> cnt(2, vector(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 prod = conv(cnt[0], cnt[1]); + rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]); + }); + return num_paths; +} int main() { cin.tie(0)->sync_with_stdio(0); for (int n = 1; n <= 100; n++) { diff --git a/tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp b/tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp index bbce584a7..8c2afaad7 100644 --- a/tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp +++ b/tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp @@ -1,7 +1,34 @@ #define PROBLEM \ "https://judge.yosupo.jp/problem/frequency_table_of_tree_distance" #include "../template.hpp" -#include "../../../library/trees/uncommon/count_paths_per_length.hpp" +#include "../../../kactl/content/numerical/FastFourierTransform.h" +#include "../../../library/trees/edge_cd.hpp" +//! @param adj unrooted, connected tree +//! @returns array `num_paths` where `num_paths[i]` = # of +//! paths in tree with `i` edges. `num_paths[1]` = # edges +//! @time O(n * logφ(n) * log2(n)) +//! @space this function allocates/returns various vectors +//! which are each O(n) +vector count_paths_per_length(const vector& adj) { + vector num_paths(sz(adj)); + if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1; + edge_cd(adj, + [&](const vector& cd_adj, int cent, int split) { + vector> cnt(2, vector(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 prod = conv(cnt[0], cnt[1]); + rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]); + }); + return num_paths; +} int main() { cin.tie(0)->sync_with_stdio(0); int n; diff --git a/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp b/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp index 1e4f02f96..9824d42af 100644 --- a/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp +++ b/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_query.test.cpp @@ -2,7 +2,99 @@ "https://judge.yosupo.jp/problem/vertex_add_range_contour_sum_on_tree" #include "../template.hpp" #include "../edge_cd_asserts.hpp" -#include "../../../library/trees/uncommon/contour_range_query.hpp" +#include "../../../library/data_structures_[l,r)/bit.hpp" +#include "../../../library/trees/edge_cd.hpp" +struct sum_adj { + int n; + vector sum, sum_ch; + vi p; + //! @param adj undirected, unrooted tree + //! @param sum sum[u] = initial number for node u + //! @time O(n) + //! @space various O(n) vectors are allocated; recursion + //! stack for dfs is O(n) + sum_adj(const vector& adj, const vector& sum): + n(sz(sum)), sum(sum), sum_ch(n), p(n, -1) { + auto dfs = [&](auto&& self, int u) -> void { + for (int c : adj[u]) + if (c != p[u]) + p[c] = u, sum_ch[u] += sum[c], self(self, c); + }; + dfs(dfs, 0); + } + //! @param u node + //! @param delta number to add + //! @time O(1) + //! @space O(1) + void update(int u, ll delta) { + sum[u] += delta; + if (p[u] != -1) sum_ch[p[u]] += delta; + } + //! @param u node + //! @returns sum of u's neighbors numbers + //! @time O(1) + //! @space O(1) + ll query(int u) { + return sum_ch[u] + (p[u] != -1 ? sum[p[u]] : 0); + } +}; +struct contour_range_query { + int n; + sum_adj sum_a; + vector>> info; + vector> bits; + //! @param adj unrooted, undirected tree + //! @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& adj, + const vector& a): + n(sz(a)), sum_a(adj, a), info(n) { + edge_cd(adj, + [&](const vector& cd_adj, int cent, int split) { + vector> sum_num(2, vector(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])}); + }); + } + //! @param u node + //! @param delta number to add to node u's number + //! @time O(logφ(n) * log2(n)) + //! @space O(1) + void update(int u, ll delta) { + sum_a.update(u, delta); + for (auto [decomp, d, side] : info[u]) + bits[decomp][side].update(d, delta); + } + //! @param u node + //! @param l,r defines range [l, r) + //! @returns sum of node v's number over all v such that + //! l <= dist(u, v) < r + //! @time O(logφ(n) * log2(n)) + //! @space O(1) + ll query(int u, int l, int r) { + ll sum = 0; + if (l <= 0 && 0 < r) sum += sum_a.sum[u]; + if (l <= 1 && 1 < r) sum += sum_a.query(u); + for (auto [decomp, d, side] : info[u]) { + auto& bit = bits[decomp][!side]; + int my_l = clamp(l - d, 1, sz(bit.s)); + int my_r = clamp(r - d, 1, sz(bit.s)); + sum += bit.query(my_l, my_r); + } + return sum; + } +}; int main() { cin.tie(0)->sync_with_stdio(0); int n, q; diff --git a/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp b/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp index da3a0bc2c..0923218a6 100644 --- a/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp +++ b/tests/library_checker_aizu_tests/trees/edge_cd_contour_range_update.test.cpp @@ -2,7 +2,97 @@ "https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree" #include "../template.hpp" #include "../edge_cd_asserts.hpp" -#include "../../../library/trees/uncommon/contour_range_update.hpp" +#include "../../../library/data_structures_[l,r)/bit_uncommon/rupq.hpp" +#include "../../../library/trees/edge_cd.hpp" +struct sum_adj { + int n; + vector sum, sum_ch; + vi p; + //! @param adj undirected, unrooted tree + //! @param sum sum[u] = initial number for node u + //! @time O(n) + //! @space various O(n) vectors are allocated; recursion + //! stack for dfs is O(n) + sum_adj(const vector& adj, const vector& sum): + n(sz(sum)), sum(sum), sum_ch(n), p(n, -1) { + auto dfs = [&](auto&& self, int u) -> void { + for (int c : adj[u]) + if (c != p[u]) + p[c] = u, sum_ch[u] += sum[c], self(self, c); + }; + dfs(dfs, 0); + } + //! @param u node + //! @param delta number to add + //! @time O(1) + //! @space O(1) + void update(int u, ll delta) { + sum[u] += delta; + if (p[u] != -1) sum_ch[p[u]] += delta; + } + //! @param u node + //! @returns sum of u's neighbors numbers + //! @time O(1) + //! @space O(1) + ll query(int u) { + return sum_ch[u] + (p[u] != -1 ? sum[p[u]] : 0); + } +}; +//! https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree +struct contour_range_update { + int n; + vector a; + sum_adj sum_a; + vector>> info; + vector> bits; + //! @param adj unrooted, undirected tree + //! @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& adj, + const vector& a): + n(sz(a)), a(a), sum_a(adj, vector(n)), info(n) { + edge_cd(adj, + [&](const vector& cd_adj, int cent, int split) { + array 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)}); + }); + } + //! @param u,l,r,delta add delta to all nodes v such + //! that l <= dist(u, v) < r + //! @time O(logφ(n) * log2(n)) + //! @space O(1) + void update(int u, int l, int r, ll delta) { + if (l <= 0 && 0 < r) a[u] += delta; + if (l <= 1 && 1 < r) sum_a.update(u, delta); + for (auto [decomp_id, d, side] : info[u]) { + auto& bit = bits[decomp_id][!side]; + int my_l = clamp(l - d, 1, bit.n); + int my_r = clamp(r - d, 1, bit.n); + bit.update(my_l, my_r, delta); + } + } + //! @param u node + //! @returns number of node u + //! @time O(logφ(n) * log2(n)) + //! @space O(1) + ll query(int u) { + ll sum = a[u] + sum_a.query(u); + for (auto [decomp_id, d, side] : info[u]) + sum += bits[decomp_id][side].get_index(d); + return sum; + } +}; int main() { cin.tie(0)->sync_with_stdio(0); int n, q; diff --git a/tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp b/tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp index 6545d13df..dcdc96b42 100644 --- a/tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp +++ b/tests/library_checker_aizu_tests/trees/edge_cd_count_paths_per_length.test.cpp @@ -2,7 +2,34 @@ "https://judge.yosupo.jp/problem/frequency_table_of_tree_distance" #include "../template.hpp" #include "../edge_cd_asserts.hpp" -#include "../../../library/trees/uncommon/count_paths_per_length.hpp" +#include "../../../kactl/content/numerical/FastFourierTransform.h" +#include "../../../library/trees/edge_cd.hpp" +//! @param adj unrooted, connected tree +//! @returns array `num_paths` where `num_paths[i]` = # of +//! paths in tree with `i` edges. `num_paths[1]` = # edges +//! @time O(n * logφ(n) * log2(n)) +//! @space this function allocates/returns various vectors +//! which are each O(n) +vector count_paths_per_length(const vector& adj) { + vector num_paths(sz(adj)); + if (sz(adj) >= 2) num_paths[1] = sz(adj) - 1; + edge_cd(adj, + [&](const vector& cd_adj, int cent, int split) { + vector> cnt(2, vector(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 prod = conv(cnt[0], cnt[1]); + rep(i, 0, sz(prod)) num_paths[i] += llround(prod[i]); + }); + return num_paths; +} int main() { cin.tie(0)->sync_with_stdio(0); int n; diff --git a/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp b/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp index 79c41cb36..93d84f552 100644 --- a/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp +++ b/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp @@ -2,7 +2,7 @@ "https://judge.yosupo.jp/problem/jump_on_tree" #include "../template.hpp" #include "../../../library/trees/uncommon/ladder_decomposition.hpp" -#include "../../../library/trees/linear_kth_par.hpp" +#include "../../../library/trees/uncommon/linear_kth_par.hpp" int main() { cin.tie(0)->sync_with_stdio(0); int n, q; diff --git a/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp b/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp index 8cf83d40d..758d89f03 100644 --- a/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp +++ b/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp @@ -2,7 +2,7 @@ "https://judge.yosupo.jp/problem/jump_on_tree" #include "../template.hpp" #include "../../../library/trees/linear_lca.hpp" -#include "../../../library/trees/linear_kth_par.hpp" +#include "../../../library/trees/uncommon/linear_kth_par.hpp" #include "../../../library/trees/lca_rmq.hpp" #include "../compress_tree_asserts.hpp" int main() { diff --git a/tests/scripts/build_pdf.sh b/tests/scripts/build_pdf.sh index e2cd8bbd3..f326b771b 100755 --- a/tests/scripts/build_pdf.sh +++ b/tests/scripts/build_pdf.sh @@ -33,11 +33,6 @@ rm ../library/monotonic_stack/max_rect_histogram.hpp || exit 1 rm ../library/monotonic_stack/cartesian_binary_tree_after.png || exit 1 rm ../library/monotonic_stack/mono_stack_ri.png || exit 1 rm ../library/strings/manacher/longest_palindrome_query.hpp || exit 1 -rm ../library/trees/uncommon/count_paths_per_length.hpp || exit 1 -rm ../library/trees/uncommon/count_paths_per_node.hpp || exit 1 -rm ../library/trees/uncommon/contour_range_query.hpp || exit 1 -rm ../library/trees/uncommon/contour_range_update.hpp || exit 1 -rm ../library/trees/uncommon/sum_adjacent.hpp || exit 1 rm ../library/trees/uncommon/ladder_decomposition.hpp || exit 1 # remove links in comments: these aren't useful in the PDF, and are usually the