#include "graph/funtional-graph.hpp"
頂点数$n$のなもりグラフを$\mathrm{O}(n\alpha (n))$で分解するライブラリ。
Namori<T>(int n)
T
add_edge(int u, int v, T w)
w
(u, v)
idx(int i)
root(int i)
vector<pair<int, T>> loop
loop[i].second
loop[i]
loop[i+1]
vector<WeightedGraph<T>> aux
aux[i]
vector<HeavyLightDecomposition<G>> hld
aux
hld[i].idx(i).first
Namori::idx(i).first
#pragma once #include <vector> #include "../data-structure/union-find.hpp" #include "../tree/heavy-light-decomposition.hpp" #include "graph-template.hpp" using namespace std; template <typename T> struct Namori { using G = WeightedGraph<T>; int n; G g; // 部分グラフ vector<G> aux; // ループ部分の(頂点,辺の重み) // loop[i].se は loop[i] と loop[i+1] の間の辺 vector<pair<int, T>> loop; // 頂点の対応関係 vector<pair<int, int>> mp; // HL分解 vector<HeavyLightDecomposition<G>> hld; Namori(int _n = 0) : _uf(_n) { init(_n); } void init(int _n) { n = _n; g.resize(n); _uf.data.resize(n); fill(begin(_uf.data), end(_uf.data), -1); _is_loop.resize(n, false); mp.resize(n, make_pair(-1, -1)); } void add_edge(int u, int v, T w = 1) { assert(_built == false); if (_uf.same(u, v)) { _root = u, _adj = v, _w = w; } else { _uf.unite(u, v); g[u].emplace_back(u, v, w); g[v].emplace_back(v, u, w); } if (++_es == n) build(); } void build() { if (_built) return; _buf.resize(n, -1); dfs1(_root, -1); for (int c = _adj; c >= 0; c = _buf[c]) { loop.emplace_back(c, -1); _is_loop[c] = true; for (auto& e : g[c]) { if (e == _buf[c]) loop.back().second = e.cost; } } assert(loop.back().first == _root); loop.back().second = _w; _h.resize(n); for (auto& [i, _] : loop) dfs2(i, -1); fill(begin(_buf), end(_buf), 0); for (auto& [i, _] : loop) dfs3(i); _uf.data.clear(); _buf.clear(); _is_loop.clear(); aux.resize(loop.size()); for (int i = 0; i < (int)loop.size(); i++) { int k = loop[i].first, j = 0; dfs4(k, i, j); aux[i].resize(j); dfs5(k); hld.emplace_back(aux[i]); } _h.clear(); _built = true; } pair<int, int> idx(int i) const { return mp[i]; } int root(int i) const { return loop[mp[i].first].first; } private: // 初期化用の状態変数 UnionFind _uf; vector<int> _buf; vector<bool> _is_loop; int _root = -1, _adj = -1, _es = 0; bool _built = false; T _w = 0; G _h; // parをメモする void dfs1(int c, int p) { for (auto& d : g[c]) { if (d != p) { _buf[d] = c; dfs1(d, c); } } } // _h で有向木を作る void dfs2(int c, int p) { for (auto& d : g[c]) { if (d == p or _is_loop[d]) continue; _h[c].emplace_back(c, d, d.cost); dfs2(d, c); } } // HLD用に順番替え void dfs3(int c) { _buf[c] = 1; for (auto& d : _h[c]) { dfs3(d); _buf[c] += _buf[d]; if (_buf[d] > _buf[_h[c][0]]) { swap(_h[c][0], d); } } } // 順番をつける void dfs4(int c, int i, int& j) { mp[c] = make_pair(i, j++); for (auto& d : _h[c]) { dfs4(d, i, j); } } // 部分グラフを作る void dfs5(int c) { for (auto& d : _h[c]) { dfs5(d); auto [i, j] = mp[c]; auto [_, k] = mp[d]; aux[i][j].emplace_back(j, k, d.cost); // 逆辺も入れたいときはここをオンにする(動くか不明) // aux[i][k].emplace_back(k, j, d.cost); } } }; /** * @brief Functional Graph(なもりグラフ)の分解 * @docs docs/graph/functional-graph.md */
#line 2 "graph/funtional-graph.hpp" #include <vector> #line 2 "data-structure/union-find.hpp" struct UnionFind { vector<int> data; UnionFind(int N) : data(N, -1) {} int find(int k) { return data[k] < 0 ? k : data[k] = find(data[k]); } int unite(int x, int y) { if ((x = find(x)) == (y = find(y))) return false; if (data[x] > data[y]) swap(x, y); data[x] += data[y]; data[y] = x; return true; } // f ... merge function template<typename F> int unite(int x, int y,const F &f) { if ((x = find(x)) == (y = find(y))) return false; if (data[x] > data[y]) swap(x, y); data[x] += data[y]; data[y] = x; f(x, y); return true; } int size(int k) { return -data[find(k)]; } int same(int x, int y) { return find(x) == find(y); } }; /** * @brief Union Find(Disjoint Set Union) * @docs docs/data-structure/union-find.md */ #line 2 "tree/heavy-light-decomposition.hpp" #line 2 "graph/graph-template.hpp" template <typename T> struct edge { int src, to; T cost; edge(int _to, T _cost) : src(-1), to(_to), cost(_cost) {} edge(int _src, int _to, T _cost) : src(_src), to(_to), cost(_cost) {} edge &operator=(const int &x) { to = x; return *this; } operator int() const { return to; } }; template <typename T> using Edges = vector<edge<T>>; template <typename T> using WeightedGraph = vector<Edges<T>>; using UnweightedGraph = vector<vector<int>>; // Input of (Unweighted) Graph UnweightedGraph graph(int N, int M = -1, bool is_directed = false, bool is_1origin = true) { UnweightedGraph g(N); if (M == -1) M = N - 1; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; if (is_1origin) x--, y--; g[x].push_back(y); if (!is_directed) g[y].push_back(x); } return g; } // Input of Weighted Graph template <typename T> WeightedGraph<T> wgraph(int N, int M = -1, bool is_directed = false, bool is_1origin = true) { WeightedGraph<T> g(N); if (M == -1) M = N - 1; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; cin >> c; if (is_1origin) x--, y--; g[x].emplace_back(x, y, c); if (!is_directed) g[y].emplace_back(y, x, c); } return g; } // Input of Edges template <typename T> Edges<T> esgraph(int N, int M, int is_weighted = true, bool is_1origin = true) { Edges<T> es; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; if (is_weighted) cin >> c; else c = 1; if (is_1origin) x--, y--; es.emplace_back(x, y, c); } return es; } // Input of Adjacency Matrix template <typename T> vector<vector<T>> adjgraph(int N, int M, T INF, int is_weighted = true, bool is_directed = false, bool is_1origin = true) { vector<vector<T>> d(N, vector<T>(N, INF)); for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; if (is_weighted) cin >> c; else c = 1; if (is_1origin) x--, y--; d[x][y] = c; if (!is_directed) d[y][x] = c; } return d; } /** * @brief グラフテンプレート * @docs docs/graph/graph-template.md */ #line 4 "tree/heavy-light-decomposition.hpp" template <typename G> struct HeavyLightDecomposition { private: void dfs_sz(int cur) { size[cur] = 1; for (auto& dst : g[cur]) { if (dst == par[cur]) { if (g[cur].size() >= 2 && int(dst) == int(g[cur][0])) swap(g[cur][0], g[cur][1]); else continue; } depth[dst] = depth[cur] + 1; par[dst] = cur; dfs_sz(dst); size[cur] += size[dst]; if (size[dst] > size[g[cur][0]]) { swap(dst, g[cur][0]); } } } void dfs_hld(int cur) { down[cur] = id++; for (auto dst : g[cur]) { if (dst == par[cur]) continue; nxt[dst] = (int(dst) == int(g[cur][0]) ? nxt[cur] : int(dst)); dfs_hld(dst); } up[cur] = id; } // [u, v) vector<pair<int, int>> ascend(int u, int v) const { vector<pair<int, int>> res; while (nxt[u] != nxt[v]) { res.emplace_back(down[u], down[nxt[u]]); u = par[nxt[u]]; } if (u != v) res.emplace_back(down[u], down[v] + 1); return res; } // (u, v] vector<pair<int, int>> descend(int u, int v) const { if (u == v) return {}; if (nxt[u] == nxt[v]) return {{down[u] + 1, down[v]}}; auto res = descend(u, par[nxt[v]]); res.emplace_back(down[nxt[v]], down[v]); return res; } public: G& g; int id; vector<int> size, depth, down, up, nxt, par; HeavyLightDecomposition(G& _g, int root = 0) : g(_g), id(0), size(g.size(), 0), depth(g.size(), 0), down(g.size(), -1), up(g.size(), -1), nxt(g.size(), root), par(g.size(), root) { dfs_sz(root); dfs_hld(root); } void build(int root) { dfs_sz(root); dfs_hld(root); } pair<int, int> idx(int i) const { return make_pair(down[i], up[i]); } template <typename F> void path_query(int u, int v, bool vertex, const F& f) { int l = lca(u, v); for (auto&& [a, b] : ascend(u, l)) { int s = a + 1, t = b; s > t ? f(t, s) : f(s, t); } if (vertex) f(down[l], down[l] + 1); for (auto&& [a, b] : descend(l, v)) { int s = a, t = b + 1; s > t ? f(t, s) : f(s, t); } } template <typename F> void path_noncommutative_query(int u, int v, bool vertex, const F& f) { int l = lca(u, v); for (auto&& [a, b] : ascend(u, l)) f(a + 1, b); if (vertex) f(down[l], down[l] + 1); for (auto&& [a, b] : descend(l, v)) f(a, b + 1); } template <typename F> void subtree_query(int u, bool vertex, const F& f) { f(down[u] + int(!vertex), up[u]); } int lca(int a, int b) { while (nxt[a] != nxt[b]) { if (down[a] < down[b]) swap(a, b); a = par[nxt[a]]; } return depth[a] < depth[b] ? a : b; } int dist(int a, int b) { return depth[a] + depth[b] - depth[lca(a, b)] * 2; } }; /** * @brief Heavy Light Decomposition(重軽分解) * @docs docs/tree/heavy-light-decomposition.md */ #line 8 "graph/funtional-graph.hpp" using namespace std; template <typename T> struct Namori { using G = WeightedGraph<T>; int n; G g; // 部分グラフ vector<G> aux; // ループ部分の(頂点,辺の重み) // loop[i].se は loop[i] と loop[i+1] の間の辺 vector<pair<int, T>> loop; // 頂点の対応関係 vector<pair<int, int>> mp; // HL分解 vector<HeavyLightDecomposition<G>> hld; Namori(int _n = 0) : _uf(_n) { init(_n); } void init(int _n) { n = _n; g.resize(n); _uf.data.resize(n); fill(begin(_uf.data), end(_uf.data), -1); _is_loop.resize(n, false); mp.resize(n, make_pair(-1, -1)); } void add_edge(int u, int v, T w = 1) { assert(_built == false); if (_uf.same(u, v)) { _root = u, _adj = v, _w = w; } else { _uf.unite(u, v); g[u].emplace_back(u, v, w); g[v].emplace_back(v, u, w); } if (++_es == n) build(); } void build() { if (_built) return; _buf.resize(n, -1); dfs1(_root, -1); for (int c = _adj; c >= 0; c = _buf[c]) { loop.emplace_back(c, -1); _is_loop[c] = true; for (auto& e : g[c]) { if (e == _buf[c]) loop.back().second = e.cost; } } assert(loop.back().first == _root); loop.back().second = _w; _h.resize(n); for (auto& [i, _] : loop) dfs2(i, -1); fill(begin(_buf), end(_buf), 0); for (auto& [i, _] : loop) dfs3(i); _uf.data.clear(); _buf.clear(); _is_loop.clear(); aux.resize(loop.size()); for (int i = 0; i < (int)loop.size(); i++) { int k = loop[i].first, j = 0; dfs4(k, i, j); aux[i].resize(j); dfs5(k); hld.emplace_back(aux[i]); } _h.clear(); _built = true; } pair<int, int> idx(int i) const { return mp[i]; } int root(int i) const { return loop[mp[i].first].first; } private: // 初期化用の状態変数 UnionFind _uf; vector<int> _buf; vector<bool> _is_loop; int _root = -1, _adj = -1, _es = 0; bool _built = false; T _w = 0; G _h; // parをメモする void dfs1(int c, int p) { for (auto& d : g[c]) { if (d != p) { _buf[d] = c; dfs1(d, c); } } } // _h で有向木を作る void dfs2(int c, int p) { for (auto& d : g[c]) { if (d == p or _is_loop[d]) continue; _h[c].emplace_back(c, d, d.cost); dfs2(d, c); } } // HLD用に順番替え void dfs3(int c) { _buf[c] = 1; for (auto& d : _h[c]) { dfs3(d); _buf[c] += _buf[d]; if (_buf[d] > _buf[_h[c][0]]) { swap(_h[c][0], d); } } } // 順番をつける void dfs4(int c, int i, int& j) { mp[c] = make_pair(i, j++); for (auto& d : _h[c]) { dfs4(d, i, j); } } // 部分グラフを作る void dfs5(int c) { for (auto& d : _h[c]) { dfs5(d); auto [i, j] = mp[c]; auto [_, k] = mp[d]; aux[i][j].emplace_back(j, k, d.cost); // 逆辺も入れたいときはここをオンにする(動くか不明) // aux[i][k].emplace_back(k, j, d.cost); } } }; /** * @brief Functional Graph(なもりグラフ)の分解 * @docs docs/graph/functional-graph.md */