This documentation is automatically generated by online-judge-tools/verification-helper
#include "graph/unicyclic_graph.hpp"
Unicyclic graph(別名: なもりグラフ)を扱う。サイクルの辺を $1$ 辺取り除いて木にして管理する。
サイクルの辺を $1$ 辺取り除いた木を返す。
サイクルの頂点番号の列を返す。取り除いた辺の両端が配列の先頭と末尾になっており、配列内の頂点は取り除いた端点のパスに登場する順序になっている。つまり、サイクルから $1$ 辺取り除いて切り開いた配列である。
取り除いた辺の一方の頂点番号を返す。
元のunicyclic graphにおける削除した辺の番号を返す。
頂点 $v$ に最も近いサイクル内の頂点番号を返す。
頂点 $u$, $v$ 間の距離を返す。HLDはサイクルから $1$ 辺取り除いた木のHLDである。
頂点 $v$ がサイクル内にあるか判定する。
#pragma once
#include <queue>
#include <stack>
#include "../graph/base.hpp"
#include "../tree/heavy_light_decomposition.hpp"
namespace ebi {
template <class T> struct unicyclic_graph {
private:
void build() {
assert(g.node_number() > 2);
assert(g.node_number() == g.edge_number());
int n = g.node_number();
in_cycle.assign(n, true);
parent.assign(n, -1);
cycle_root.assign(n, -1);
// サイクルの検出
{
std::vector<int> deg(n);
std::stack<int> stack;
for (auto i : std::views::iota(0, n)) {
deg[i] = g[i].size();
if (deg[i] == 1) {
stack.push(i);
}
}
while (!stack.empty()) {
int v = stack.top();
stack.pop();
in_cycle[v] = false;
for (auto e : g[v]) {
if (deg[e.to] == 1) continue;
parent[v] = e.to;
deg[e.to]--;
if (deg[e.to] == 1) {
stack.push(e.to);
}
}
}
}
// サイクルの 1 辺を切って root から順に並べる
for (auto i : std::views::iota(0, n)) {
if (in_cycle[i]) {
root = i;
for (auto e : g[i]) {
if (in_cycle[e.to]) {
remove_edge_id = e.id;
break;
}
}
break;
}
}
std::vector<bool> valid(n, false);
cycle = {root};
while (!valid[cycle.back()]) {
int v = cycle.back();
cycle_root[v] = v;
valid[v] = true;
for (auto e : g[v]) {
if (e.id == remove_edge_id || valid[e.to] || !in_cycle[e.to])
continue;
cycle.emplace_back(e.to);
parent[e.to] = v;
}
}
std::queue<int> que;
for (auto v : cycle) que.push(v);
while (!que.empty()) {
int v = que.front();
que.pop();
for (auto e : g[v]) {
if (cycle_root[e.to] != -1) continue;
cycle_root[e.to] = cycle_root[v];
que.push(e.to);
}
}
}
public:
unicyclic_graph(const Graph<T> &g_) : g(g_) {
build();
}
Graph<T> get_tree() const {
Graph<T> tree(g.node_number());
for (auto e : g.get_edges()) {
if (e.id == remove_edge_id) continue;
tree.add_undirected_edge(e.from, e.to, e.cost);
}
tree.build();
return tree;
}
std::vector<int> get_cycle() const {
return cycle;
}
int get_root() const {
return root;
}
int get_remove_edge_id() const {
return remove_edge_id;
}
int get_cycle_root(int v) const {
return cycle_root[v];
}
T distance(const heavy_light_decomposition<T> &hld, int u, int v) const {
if (cycle_root[u] == cycle_root[v]) return hld.distance(u, v);
T loop =
hld.distance(root, cycle.back()) + g.get_edge(remove_edge_id).cost;
T d = hld.distance(cycle_root[u], cycle_root[v]);
d = std::min<T>(d, loop - d);
return hld.distance(cycle_root[u], u) + hld.distance(cycle_root[v], v) +
d;
}
bool is_in_cycle(int v) const {
return in_cycle[v];
}
private:
int root;
int remove_edge_id;
Graph<T> g, tree;
std::vector<int> cycle, parent, cycle_root;
std::vector<bool> in_cycle;
};
} // namespace ebi
#line 2 "graph/unicyclic_graph.hpp"
#include <queue>
#include <stack>
#line 2 "graph/base.hpp"
#include <cassert>
#include <iostream>
#include <ranges>
#include <vector>
#line 2 "data_structure/simple_csr.hpp"
#line 4 "data_structure/simple_csr.hpp"
#include <utility>
#line 6 "data_structure/simple_csr.hpp"
namespace ebi {
template <class E> struct simple_csr {
simple_csr() = default;
simple_csr(int n, const std::vector<std::pair<int, E>>& elements)
: start(n + 1, 0), elist(elements.size()) {
for (auto e : elements) {
start[e.first + 1]++;
}
for (auto i : std::views::iota(0, n)) {
start[i + 1] += start[i];
}
auto counter = start;
for (auto [i, e] : elements) {
elist[counter[i]++] = e;
}
}
simple_csr(const std::vector<std::vector<E>>& es)
: start(es.size() + 1, 0) {
int n = es.size();
for (auto i : std::views::iota(0, n)) {
start[i + 1] = (int)es[i].size() + start[i];
}
elist.resize(start.back());
for (auto i : std::views::iota(0, n)) {
std::copy(es[i].begin(), es[i].end(), elist.begin() + start[i]);
}
}
int size() const {
return (int)start.size() - 1;
}
const auto operator[](int i) const {
return std::ranges::subrange(elist.begin() + start[i],
elist.begin() + start[i + 1]);
}
auto operator[](int i) {
return std::ranges::subrange(elist.begin() + start[i],
elist.begin() + start[i + 1]);
}
const auto operator()(int i, int l, int r) const {
return std::ranges::subrange(elist.begin() + start[i] + l,
elist.begin() + start[i + 1] + r);
}
auto operator()(int i, int l, int r) {
return std::ranges::subrange(elist.begin() + start[i] + l,
elist.begin() + start[i + 1] + r);
}
private:
std::vector<int> start;
std::vector<E> elist;
};
} // namespace ebi
#line 9 "graph/base.hpp"
namespace ebi {
template <class T> struct Edge {
int from, to;
T cost;
int id;
};
template <class E> struct Graph {
using cost_type = E;
using edge_type = Edge<cost_type>;
Graph(int n_) : n(n_) {}
Graph() = default;
void add_edge(int u, int v, cost_type c) {
assert(!prepared && u < n && v < n);
buff.emplace_back(u, edge_type{u, v, c, m});
edges.emplace_back(edge_type{u, v, c, m++});
}
void add_undirected_edge(int u, int v, cost_type c) {
assert(!prepared && u < n && v < n);
buff.emplace_back(u, edge_type{u, v, c, m});
buff.emplace_back(v, edge_type{v, u, c, m});
edges.emplace_back(edge_type{u, v, c, m});
m++;
}
void read_tree(int offset = 1, bool is_weighted = false) {
read_graph(n - 1, offset, false, is_weighted);
}
void read_parents(int offset = 1) {
for (auto i : std::views::iota(1, n)) {
int p;
std::cin >> p;
p -= offset;
add_undirected_edge(p, i, 1);
}
build();
}
void read_graph(int e, int offset = 1, bool is_directed = false,
bool is_weighted = false) {
for (int i = 0; i < e; i++) {
int u, v;
std::cin >> u >> v;
u -= offset;
v -= offset;
if (is_weighted) {
cost_type c;
std::cin >> c;
if (is_directed) {
add_edge(u, v, c);
} else {
add_undirected_edge(u, v, c);
}
} else {
if (is_directed) {
add_edge(u, v, 1);
} else {
add_undirected_edge(u, v, 1);
}
}
}
build();
}
void build() {
assert(!prepared);
csr = simple_csr<edge_type>(n, buff);
buff.clear();
prepared = true;
}
int size() const {
return n;
}
int node_number() const {
return n;
}
int edge_number() const {
return m;
}
edge_type get_edge(int i) const {
assert(prepared);
return edges[i];
}
std::vector<edge_type> get_edges() const {
assert(prepared);
return edges;
}
const auto operator[](int i) const {
assert(prepared);
return csr[i];
}
auto operator[](int i) {
assert(prepared);
return csr[i];
}
private:
int n, m = 0;
std::vector<std::pair<int, edge_type>> buff;
std::vector<edge_type> edges;
simple_csr<edge_type> csr;
bool prepared = false;
};
} // namespace ebi
#line 2 "tree/heavy_light_decomposition.hpp"
#include <algorithm>
#line 6 "tree/heavy_light_decomposition.hpp"
#line 8 "tree/heavy_light_decomposition.hpp"
namespace ebi {
template <class T> struct heavy_light_decomposition {
private:
void dfs_sz(int v) {
for (auto &e : g[v]) {
if (e.to == par[v]) continue;
par[e.to] = v;
depth_[e.to] = depth_[v] + 1;
dist[e.to] = dist[v] + e.cost;
dfs_sz(e.to);
sz[v] += sz[e.to];
if (sz[e.to] > sz[g[v][0].to] || g[v][0].to == par[v])
std::swap(e, g[v][0]);
}
}
void dfs_hld(int v) {
in[v] = num++;
rev[in[v]] = v;
for (auto e : g[v]) {
if (e.to == par[v]) continue;
nxt[e.to] = (e.to == g[v][0].to ? nxt[v] : e.to);
dfs_hld(e.to);
}
out[v] = num;
}
// [u, v) パスの取得 (v は u の祖先)
std::vector<std::pair<int, int>> ascend(int u, int v) const {
std::vector<std::pair<int, int>> res;
while (nxt[u] != nxt[v]) {
res.emplace_back(in[u], in[nxt[u]]);
u = par[nxt[u]];
}
if (u != v) res.emplace_back(in[u], in[v] + 1);
return res;
}
// (u, v] パスの取得 (u は v の祖先)
std::vector<std::pair<int, int>> descend(int u, int v) const {
if (u == v) return {};
if (nxt[u] == nxt[v]) return {{in[u] + 1, in[v]}};
auto res = descend(u, par[nxt[v]]);
res.emplace_back(in[nxt[v]], in[v]);
return res;
}
public:
heavy_light_decomposition(const Graph<T> &gh, int root_ = 0)
: n(gh.size()),
root(root_),
g(gh),
sz(n, 1),
in(n),
out(n),
nxt(n),
par(n, -1),
depth_(n, 0),
rev(n),
dist(n, 0) {
nxt[root] = root;
dfs_sz(root);
dfs_hld(root);
}
int idx(int u) const {
return in[u];
}
int rev_idx(int i) const {
return rev[i];
}
int la(int v, int k) const {
while (1) {
int u = nxt[v];
if (in[u] <= in[v] - k) return rev[in[v] - k];
k -= in[v] - in[u] + 1;
v = par[u];
}
}
int lca(int u, int v) const {
while (nxt[u] != nxt[v]) {
if (in[u] < in[v]) std::swap(u, v);
u = par[nxt[u]];
}
return depth_[u] < depth_[v] ? u : v;
}
int jump(int s, int t, int i) const {
if (i == 0) return s;
int l = lca(s, t);
int d = depth_[s] + depth_[t] - depth_[l] * 2;
if (d < i) return -1;
if (depth_[s] - depth_[l] >= i) return la(s, i);
i = d - i;
return la(t, i);
}
std::vector<int> path(int s, int t) const {
int l = lca(s, t);
std::vector<int> a, b;
for (; s != l; s = par[s]) a.emplace_back(s);
for (; t != l; t = par[t]) b.emplace_back(t);
a.emplace_back(l);
std::reverse(b.begin(), b.end());
a.insert(a.end(), b.begin(), b.end());
return a;
}
int root_of_heavy_path(int u) const {
return nxt[u];
}
int parent(int u) const {
return par[u];
}
T distance(int u, int v) const {
return dist[u] + dist[v] - 2 * dist[lca(u, v)];
}
T distance_from_root(int v) const {
return dist[v];
}
T depth(int v) const {
return depth_[v];
}
bool at_path(int u, int v, int s) const {
return distance(u, v) == distance(u, s) + distance(s, v);
}
std::pair<int, int> subtree_section(int v) const {
return {in[v], out[v]};
}
bool is_subtree(int u, int v) const {
return in[u] <= in[v] && in[v] < out[u];
}
template <class F>
void path_noncommutative_query(int u, int v, bool vertex,
const F &f) const {
int l = lca(u, v);
for (auto [a, b] : ascend(u, l)) f(a + 1, b);
if (vertex) f(in[l], in[l] + 1);
for (auto [a, b] : descend(l, v)) f(a, b + 1);
}
std::vector<std::pair<int, int>> path_sections(int u, int v,
bool vertex) const {
int l = lca(u, v);
std::vector<std::pair<int, int>> sections;
for (auto [a, b] : ascend(u, l)) sections.emplace_back(a + 1, b);
if (vertex) sections.emplace_back(in[l], in[l] + 1);
for (auto [a, b] : descend(l, v)) sections.emplace_back(a, b + 1);
return sections;
}
template <class F>
int max_path(int u, int v, bool vertex, F binary_search) const {
int prev = -1;
int l = lca(u, v);
for (auto [a, b] : ascend(u, l)) {
a++;
int m = binary_search(a, b);
if (m == b) {
prev = rev[b];
} else {
return (m == a ? prev : rev[m]);
}
}
if (vertex) {
int m = binary_search(in[l], in[l] + 1);
if (m == in[l]) {
return prev;
} else {
prev = l;
}
}
for (auto [a, b] : descend(l, v)) {
b++;
int m = binary_search(a, b);
if (m == b) {
prev = rev[b - 1];
} else {
return m == a ? prev : rev[m - 1];
}
}
return v;
}
template <class F> void subtree_query(int u, bool vertex, const F &f) {
f(in[u] + int(!vertex), out[u]);
}
const std::vector<int> &dfs_order() const {
return rev;
}
template <class ADD, class QUERY, class CLEAR, class RESET>
void dsu_on_tree(const ADD &add, const QUERY &query, const CLEAR &clear,
const RESET &reset) const;
std::vector<std::pair<int, int>> lca_based_auxiliary_tree_dfs_order(
std::vector<int> vs) const;
std::pair<std::vector<int>, Graph<T>> lca_based_auxiliary_tree(
std::vector<int> vs) const;
private:
int n, root;
Graph<T> g;
std::vector<int> sz, in, out, nxt, par, depth_, rev;
std::vector<T> dist;
int num = 0;
};
} // namespace ebi
#line 8 "graph/unicyclic_graph.hpp"
namespace ebi {
template <class T> struct unicyclic_graph {
private:
void build() {
assert(g.node_number() > 2);
assert(g.node_number() == g.edge_number());
int n = g.node_number();
in_cycle.assign(n, true);
parent.assign(n, -1);
cycle_root.assign(n, -1);
// サイクルの検出
{
std::vector<int> deg(n);
std::stack<int> stack;
for (auto i : std::views::iota(0, n)) {
deg[i] = g[i].size();
if (deg[i] == 1) {
stack.push(i);
}
}
while (!stack.empty()) {
int v = stack.top();
stack.pop();
in_cycle[v] = false;
for (auto e : g[v]) {
if (deg[e.to] == 1) continue;
parent[v] = e.to;
deg[e.to]--;
if (deg[e.to] == 1) {
stack.push(e.to);
}
}
}
}
// サイクルの 1 辺を切って root から順に並べる
for (auto i : std::views::iota(0, n)) {
if (in_cycle[i]) {
root = i;
for (auto e : g[i]) {
if (in_cycle[e.to]) {
remove_edge_id = e.id;
break;
}
}
break;
}
}
std::vector<bool> valid(n, false);
cycle = {root};
while (!valid[cycle.back()]) {
int v = cycle.back();
cycle_root[v] = v;
valid[v] = true;
for (auto e : g[v]) {
if (e.id == remove_edge_id || valid[e.to] || !in_cycle[e.to])
continue;
cycle.emplace_back(e.to);
parent[e.to] = v;
}
}
std::queue<int> que;
for (auto v : cycle) que.push(v);
while (!que.empty()) {
int v = que.front();
que.pop();
for (auto e : g[v]) {
if (cycle_root[e.to] != -1) continue;
cycle_root[e.to] = cycle_root[v];
que.push(e.to);
}
}
}
public:
unicyclic_graph(const Graph<T> &g_) : g(g_) {
build();
}
Graph<T> get_tree() const {
Graph<T> tree(g.node_number());
for (auto e : g.get_edges()) {
if (e.id == remove_edge_id) continue;
tree.add_undirected_edge(e.from, e.to, e.cost);
}
tree.build();
return tree;
}
std::vector<int> get_cycle() const {
return cycle;
}
int get_root() const {
return root;
}
int get_remove_edge_id() const {
return remove_edge_id;
}
int get_cycle_root(int v) const {
return cycle_root[v];
}
T distance(const heavy_light_decomposition<T> &hld, int u, int v) const {
if (cycle_root[u] == cycle_root[v]) return hld.distance(u, v);
T loop =
hld.distance(root, cycle.back()) + g.get_edge(remove_edge_id).cost;
T d = hld.distance(cycle_root[u], cycle_root[v]);
d = std::min<T>(d, loop - d);
return hld.distance(cycle_root[u], u) + hld.distance(cycle_root[v], v) +
d;
}
bool is_in_cycle(int v) const {
return in_cycle[v];
}
private:
int root;
int remove_edge_id;
Graph<T> g, tree;
std::vector<int> cycle, parent, cycle_root;
std::vector<bool> in_cycle;
};
} // namespace ebi