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
32 changes: 14 additions & 18 deletions library/data_structures/dsu/dsu_bipartite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,33 @@
//! bipartite check
struct dsu_bipartite {
int num_sets;
struct node {
int p = -1;
bool is_bi = 1, parity;
};
vector<node> t;
dsu_bipartite(int n): num_sets(n), t(n) {}
vi p, is_bi, parity;
dsu_bipartite(int n):
num_sets(n), p(n, -1), is_bi(n, 1), parity(n) {}
int find(int v) {
if (t[v].p < 0) return v;
int root = find(t[v].p);
t[v].parity ^= t[t[v].p].parity;
return t[v].p = root;
if (p[v] < 0) return v;
int root = find(p[v]);
parity[v] ^= parity[p[v]];
return p[v] = root;
}
bool join(int u, int v) {
int root_u = find(u), root_v = find(v);
if (root_u == root_v) {
if (t[u].parity == t[v].parity) t[root_u].is_bi = 0;
if (parity[u] == parity[v]) is_bi[root_u] = 0;
return 0;
}
if (t[root_u].p > t[root_v].p) {
if (p[root_u] > p[root_v]) {
swap(u, v);
swap(root_u, root_v);
}
t[root_u].is_bi &= t[root_v].is_bi;
t[root_v].parity = t[v].parity ^ 1 ^ t[u].parity;
t[root_u].p += t[root_v].p, t[root_v].p = root_u,
num_sets--;
is_bi[root_u] &= is_bi[root_v];
parity[root_v] = parity[v] ^ 1 ^ parity[u];
p[root_u] += p[root_v], p[root_v] = root_u, num_sets--;
return 1;
}
int size(int v) { return -t[find(v)].p; }
int size(int v) { return -p[find(v)]; }
bool same_set(int u, int v) {
return find(u) == find(v);
}
bool is_bipartite(int v) { return t[find(v)].is_bi; }
bool is_bipartite(int v) { return is_bi[find(v)]; }
};
2 changes: 1 addition & 1 deletion library/trees/centroid_decomp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ template<class F, class G> struct centroid {
vi siz;
centroid(const G& adj, F f):
adj(adj), f(f), siz(sz(adj), -1) {
rep(i, 0, sz(adj)) if (siz[i] == -1) dfs(i, -1);
dfs(0, -1);
}
void calc_sz(int v, int p) {
siz[v] = 1;
Expand Down
6 changes: 3 additions & 3 deletions library/trees/extra_members/compress_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
//! @time O(|subset| log |subset|)
//! @space O(|subset|)
array<vi, 2> compress_tree(vi subset) {
auto proj = [&](int v) { return t[v].in; };
auto proj = [&](int v) { return in[v]; };
ranges::sort(subset, {}, proj);
int siz = sz(subset);
rep(i, 1, siz)
int len = sz(subset);
rep(i, 1, len)
subset.push_back(lca(subset[i - 1], subset[i]));
ranges::sort(subset, {}, proj);
subset.erase(unique(all(subset)), end(subset));
Expand Down
4 changes: 2 additions & 2 deletions library/trees/extra_members/dist_edges.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
int dist_edges(int u, int v) {
return t[u].d + t[v].d - 2 * t[lca(u, v)].d;
int dist(int u, int v) {
return d[u] + d[v] - 2 * d[lca(u, v)];
}
3 changes: 1 addition & 2 deletions library/trees/extra_members/in_subtree.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! returns 1 if v is in u's subtree
bool in_subtree(int u, int v) {
return t[u].in <= t[v].in &&
t[v].in < t[u].in + t[u].sub_sz;
return in[u] <= in[v] && in[v] < in[u] + siz[u];
}
43 changes: 21 additions & 22 deletions library/trees/ladder_decomposition/ladder_decomposition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,36 @@
//! @code
//! ladder ld(adj);
//! // KACTL functions
//! int kth_par = jmp(ld.b_tbl, v, k);
//! int curr_lca = lca(ld.b_tbl, ld.d, u, v);
//! int kth_par = jmp(ld.jmp, v, k);
//! int curr_lca = lca(ld.jmp, ld.d, u, v);
//! @endcode
struct ladder {
int n;
vector<vi> b_tbl;
vi d, p, dl, idx_l, l_tbl;
vi d, p, leaf, idx, lad;
vector<vi> jmp;
//! @param adj forest (rooted or unrooted)
//! @time O(n log n)
//! @space O(n log n) for b_tbl. Everything else is O(n)
//! @space O(n log n) for jmp. Everything else is O(n)
ladder(const auto& adj):
n(sz(adj)), d(n), p(n, -1), dl(n), idx_l(n) {
n(sz(adj)), d(n), p(n), leaf(n), idx(n), lad(2 * n) {
auto dfs = [&](auto&& self, int v) -> void {
dl[v] = v;
leaf[v] = v;
for (int u : adj[v])
if (u != p[v]) {
d[u] = d[p[u] = v] + 1;
self(self, u);
if (d[dl[u]] > d[dl[v]]) dl[v] = dl[u];
if (d[leaf[v]] < d[leaf[u]]) leaf[v] = leaf[u];
}
};
rep(i, 0, n) {
if (p[i] == -1) p[i] = i, dfs(dfs, i);
if (p[i] == i || dl[p[i]] != dl[i]) {
int v = dl[i], len = (d[v] - d[i]) * 2;
idx_l[v] = sz(l_tbl) + d[v];
for (; v != -1 && len--; v = p[v])
l_tbl.push_back(v);
}
dfs(dfs, 0);
int pos = 0;
rep(i, 0, n) if (p[i] == i || leaf[p[i]] != leaf[i]) {
int l = leaf[i];
int len = min((d[l] - d[i]) * 2, d[l] + 1);
idx[l] = pos;
for (; len--; l = p[l]) lad[pos++] = l;
}
b_tbl = treeJump(p);
jmp = treeJump(p);
}
//! @param v query node
//! @param k number of edges
Expand All @@ -47,11 +46,11 @@ struct ladder {
assert(0 <= k && k <= d[v]);
if (k == 0) return v;
int bit = __lg(k);
v = b_tbl[bit][v], k -= (1 << bit);
int l = idx_l[dl[v]] - d[v];
assert(l_tbl[l] == v);
// subarray [l, l+k] of l_tbl corresponds to the rest
v = jmp[bit][v], k -= (1 << bit);
int l = idx[leaf[v]] + d[leaf[v]] - d[v];
assert(lad[l] == v);
// subarray [l, l+k] of lad corresponds to the rest
// of the jump
return l_tbl[l + k];
return lad[l + k];
}
};
28 changes: 13 additions & 15 deletions library/trees/lca_rmq/lca_rmq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,27 @@
//! @space O(nlogn)
// NOLINTNEXTLINE(readability-identifier-naming)
struct LCA {
struct node {
int in, sub_sz = 1, d, p = -1;
};
vector<node> t;
int n;
vi in, siz, d, p;
RMQ<int, function<int(int, int)>> rmq = {{}, NULL};
LCA(const auto& adj): t(sz(adj)) {
LCA(const auto& adj):
n(sz(adj)), in(n), siz(n, 1), d(n), p(n) {
vi order;
auto dfs = [&](auto&& self, int v) -> void {
t[v].in = sz(order), order.push_back(v);
in[v] = sz(order), order.push_back(v);
for (int u : adj[v])
if (u != t[v].p)
t[u].d = t[t[u].p = v].d + 1, self(self, u),
t[v].sub_sz += t[u].sub_sz;
if (u != p[v])
d[u] = d[p[u] = v] + 1, self(self, u),
siz[v] += siz[u];
};
rep(i, 0, sz(t)) if (t[i].p == -1) dfs(dfs, i);
rmq = {order, [&](int u, int v) {
return t[u].d < t[v].d ? u : v;
}};
dfs(dfs, 0);
rmq = {order,
[&](int u, int v) { return d[u] < d[v] ? u : v; }};
}
int lca(int u, int v) {
if (u == v) return u;
auto [x, y] = minmax(t[u].in, t[v].in);
return t[rmq.query(x + 1, y + 1)].p;
auto [x, y] = minmax(in[u], in[v]);
return p[rmq.query(x + 1, y + 1)];
}
#include "../extra_members/dist_edges.hpp"
#include "../extra_members/in_subtree.hpp"
Expand Down
5 changes: 2 additions & 3 deletions library/trees/lca_rmq/next_on_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//! @space O(1)
int next_on_path(int u, int v) {
assert(u != v);
return in_subtree(u, v)
? rmq.query(t[u].in + 1, t[v].in + 1)
: t[u].p;
return in_subtree(u, v) ? rmq.query(in[u] + 1, in[v] + 1)
: p[u];
}
1 change: 1 addition & 0 deletions library/trees/linear_lca.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ struct linear_lca {
}
return d[u] < d[v] ? u : v;
}
#include "extra_members/dist_edges.hpp"
};
2 changes: 1 addition & 1 deletion library/trees/subtree_isomorphism.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ auto subtree_iso(const auto& adj) {
hashes.try_emplace(ch_ids, sz(hashes))
.first->second;
};
rep(i, 0, sz(adj)) if (iso_id[i] == -1) dfs(dfs, i, i);
dfs(dfs, 0, 0);
return pair{sz(hashes), iso_id};
}
13 changes: 0 additions & 13 deletions library/trees/tree_lift/kth_path.hpp

This file was deleted.

39 changes: 15 additions & 24 deletions library/trees/tree_lift/tree_lift.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,35 @@
//! }
//! vector<basic_string<int>> adj(n);
//! tree_lift tree_l(adj);
//! int kth_p = tree_l.kth_par(v, k);
//! tree_l.kth_par(v, k); // k edges up from v
//! @endcode
//! kth_p = a node k edges up from v
//! @time O(n + q log n)
//! @space O(n)
struct tree_lift {
struct node {
int d, p = -1, j = -1;
};
vector<node> t;
tree_lift(const auto& adj): t(sz(adj)) {
vi d, p, j;
tree_lift(const auto& adj): d(sz(adj)), p(d), j(d) {
auto dfs = [&](auto&& self, int v) -> void {
int jump =
(t[v].d + t[t[t[v].j].j].d == 2 * t[t[v].j].d)
? t[t[v].j].j
: v;
int up =
d[v] + d[j[j[v]]] == 2 * d[j[v]] ? j[j[v]] : v;
for (int u : adj[v])
if (u != t[v].p)
t[u].d = t[t[u].p = v].d + 1, t[u].j = jump,
self(self, u);
if (u != p[v])
d[u] = d[p[u] = v] + 1, j[u] = up, self(self, u);
};
rep(i, 0, sz(t)) if (t[i].j == -1) t[i].j = i,
dfs(dfs, i);
dfs(dfs, 0);
}
int kth_par(int v, int k) {
int anc_d = t[v].d - k;
while (t[v].d > anc_d)
v = t[t[v].j].d >= anc_d ? t[v].j : t[v].p;
int anc_d = d[v] - k;
while (d[v] > anc_d)
v = d[j[v]] >= anc_d ? j[v] : p[v];
return v;
}
int lca(int u, int v) {
if (t[u].d < t[v].d) swap(u, v);
u = kth_par(u, t[u].d - t[v].d);
if (d[u] < d[v]) swap(u, v);
u = kth_par(u, d[u] - d[v]);
while (u != v)
if (t[u].j != t[v].j) u = t[u].j, v = t[v].j;
else u = t[u].p, v = t[v].p;
if (j[u] != j[v]) u = j[u], v = j[v];
else u = p[u], v = p[v];
return u;
}
#include "../extra_members/dist_edges.hpp"
#include "kth_path.hpp"
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,35 @@
#include "../template.hpp"
#include "../../../library/contest/random.hpp"
#include "../../../library/trees/centroid_decomp_uncommon/count_paths_per_node.hpp"
#include "../../../library/data_structures/dsu/dsu_restorable.hpp"
#include "../../../library/trees/tree_lift/tree_lift.hpp"
#include "../../../library/trees/lca_rmq/lca_rmq.hpp"
#include "../../../library/trees/centroid_decomp_uncommon/count_paths_per_length.hpp"
#include "../cd_asserts.hpp"
vector<vector<ll>> naive(const vector<vector<int>>& adj,
dsu_restorable& dsu) {
tree_lift tl(adj);
vector<vector<ll>> naive(const vector<vi>& adj) {
LCA lc(adj);
int n = sz(adj);
vector<vector<ll>> cnts_naive(n + 1, vector<ll>(n, 0));
for (int u = 0; u < n; u++) {
for (int v = u; v < n; v++) {
if (dsu.same_set(u, v)) {
int path_length_edges = tl.dist_edges(u, v);
for (int i = 0; i <= path_length_edges; i++)
cnts_naive[path_length_edges]
[tl.kth_path(u, v, i)]++;
}
int path_length_edges = lc.dist(u, v);
for (int node = u; node != v;
node = lc.next_on_path(node, v))
cnts_naive[path_length_edges][node]++;
cnts_naive[path_length_edges][v]++;
}
}
return cnts_naive;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
for (int n = 1; n <= 100; n++) {
vector<vector<int>> adj(n);
dsu_restorable dsu(n);
for (int i = 0; i < n - 2; i++) {
int u = rnd<int>(0, n - 1);
int v = rnd<int>(0, n - 1);
if (u == v) continue;
if (dsu.join(u, v)) {
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<vi> adj(n);
for (int i = 1; i < n; i++) {
int par = rnd<int>(0, i - 1);
adj[par].push_back(i);
adj[i].push_back(par);
}
cd_asserts(adj);
vector<vector<ll>> cnts_naive = naive(adj, dsu);
vector<vector<ll>> cnts_naive = naive(adj);
for (int k = 1; k <= n; k++)
assert(
count_paths_per_node(adj, k) == cnts_naive[k]);
Expand Down
Loading
Loading