This documentation is automatically generated by online-judge-tools/verification-helper
#include "data_structure/offline_dynamic_connectivity.hpp"
オフライン版の dynamic connectivity (の抽象化実装).
以下の性質を満たす何らかのオブジェクト $x$ と,$x$ に対するさまざまな作用たち $f_i$ を考えたい:
このとき、本コードは以下のオフラインクエリ群の高速処理をサポートする.
汎用化のため具体的なオブジェクト $x$ や作用 $f$ たちの管理は本コードでは行っておらず,実装者が UnionFind などを別途用意する必要がある.
入力に登場する時刻 $t$ を列挙し二分木を構築する.作用クエリをセグメント木の要領で頂点に効率的に配置し,二分木を DFS しながら作用の演算・undo 操作・特徴量取得を行えばよい.
// Tick の型は std::lower_bound 等による二分探索ができればなんでもよい(std::pair, std::tuple なども可)
offline_dynamic_connectivity<int> dc;
dc.apply_time_range(0, 100, 1); // 時刻 [0, 100) の範囲で辺番号 1 を追加
dc.add_observation(50, 2); // 時刻 50 に取得クエリ 2 を追加
for (auto p : dc.generate()) {
if (p.op == DyConOperation::Begins) {
int edge_id = p.id_;
// 辺番号 edge_id を張る
} else if (p.op == DyConOperation::Ends) {
// undo 操作を一回行う
} else if (p.op == DyConOperation::Event) {
int query_id = p.id_;
// 取得クエリ query_id を行う
}
}
オブジェクト $x$ を $n$ 頂点の無向グラフとして,作用として
の 2 つ,特徴量として「連結成分の頂点重み和」を考えると,これは undo 可能 UnionFind で効率的に処理できる.よって本ライブラリを利用することで高速に解ける(「問題例」の “Dynamic Graph Vertex Add Component Sum” が該当).
#pragma once
#include <algorithm>
#include <limits>
#include <set>
#include <utility>
#include <vector>
enum class DyConOperation {
Begins = 1,
Ends = 2,
Event = 3,
};
template <class Tick = int> struct offline_dynamic_connectivity {
std::vector<std::pair<Tick, int>> ops;
struct Edge {
Tick since;
Tick until;
int edge_id;
};
std::vector<Edge> edges;
offline_dynamic_connectivity() = default;
void add_observation(Tick clk, int event_id) { ops.emplace_back(clk, event_id); }
void apply_time_range(Tick since, Tick until, int edge_id) {
edges.push_back(Edge{since, until, edge_id});
}
struct Procedure {
DyConOperation op;
int id_;
};
std::vector<std::vector<Procedure>> nodes;
std::vector<Procedure> ret_;
void rec(int now) {
ret_.insert(ret_.end(), nodes[now].cbegin(), nodes[now].cend());
if (now * 2 < int(nodes.size())) rec(now * 2);
if (now * 2 + 1 < int(nodes.size())) rec(now * 2 + 1);
for (auto itr = nodes[now].rbegin(); itr != nodes[now].rend(); ++itr) {
if (itr->op == DyConOperation::Begins) {
ret_.push_back(Procedure{DyConOperation::Ends, itr->id_});
}
}
}
std::vector<Procedure> generate() {
if (ops.empty()) return {};
std::vector<Tick> query_ts;
{
query_ts.reserve(ops.size());
for (const auto &p : ops) query_ts.push_back(p.first);
std::sort(query_ts.begin(), query_ts.end());
query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
std::vector<int> t_use(query_ts.size() + 1);
t_use.at(0) = 1;
for (const Edge &e : edges) {
t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin()] =
1;
t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin()] =
1;
}
for (int i = 1; i < int(query_ts.size()); ++i) {
if (!t_use[i]) query_ts[i] = query_ts[i - 1];
}
query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
}
const int N = query_ts.size();
int D = 1;
while (D < int(query_ts.size())) D *= 2;
nodes.assign(D + N, {});
for (const Edge &e : edges) {
int l =
D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin());
int r =
D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin());
while (l < r) {
if (l & 1) nodes[l++].push_back(Procedure{DyConOperation::Begins, e.edge_id});
if (r & 1) nodes[--r].push_back(Procedure{DyConOperation::Begins, e.edge_id});
l >>= 1, r >>= 1;
}
}
for (const auto &op : ops) {
int t = std::upper_bound(query_ts.begin(), query_ts.end(), op.first) - query_ts.begin();
nodes.at(t + D - 1).push_back(Procedure{DyConOperation::Event, op.second});
}
ret_.clear();
rec(1);
return ret_;
}
};
#line 2 "data_structure/offline_dynamic_connectivity.hpp"
#include <algorithm>
#include <limits>
#include <set>
#include <utility>
#include <vector>
enum class DyConOperation {
Begins = 1,
Ends = 2,
Event = 3,
};
template <class Tick = int> struct offline_dynamic_connectivity {
std::vector<std::pair<Tick, int>> ops;
struct Edge {
Tick since;
Tick until;
int edge_id;
};
std::vector<Edge> edges;
offline_dynamic_connectivity() = default;
void add_observation(Tick clk, int event_id) { ops.emplace_back(clk, event_id); }
void apply_time_range(Tick since, Tick until, int edge_id) {
edges.push_back(Edge{since, until, edge_id});
}
struct Procedure {
DyConOperation op;
int id_;
};
std::vector<std::vector<Procedure>> nodes;
std::vector<Procedure> ret_;
void rec(int now) {
ret_.insert(ret_.end(), nodes[now].cbegin(), nodes[now].cend());
if (now * 2 < int(nodes.size())) rec(now * 2);
if (now * 2 + 1 < int(nodes.size())) rec(now * 2 + 1);
for (auto itr = nodes[now].rbegin(); itr != nodes[now].rend(); ++itr) {
if (itr->op == DyConOperation::Begins) {
ret_.push_back(Procedure{DyConOperation::Ends, itr->id_});
}
}
}
std::vector<Procedure> generate() {
if (ops.empty()) return {};
std::vector<Tick> query_ts;
{
query_ts.reserve(ops.size());
for (const auto &p : ops) query_ts.push_back(p.first);
std::sort(query_ts.begin(), query_ts.end());
query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
std::vector<int> t_use(query_ts.size() + 1);
t_use.at(0) = 1;
for (const Edge &e : edges) {
t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin()] =
1;
t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin()] =
1;
}
for (int i = 1; i < int(query_ts.size()); ++i) {
if (!t_use[i]) query_ts[i] = query_ts[i - 1];
}
query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
}
const int N = query_ts.size();
int D = 1;
while (D < int(query_ts.size())) D *= 2;
nodes.assign(D + N, {});
for (const Edge &e : edges) {
int l =
D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin());
int r =
D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin());
while (l < r) {
if (l & 1) nodes[l++].push_back(Procedure{DyConOperation::Begins, e.edge_id});
if (r & 1) nodes[--r].push_back(Procedure{DyConOperation::Begins, e.edge_id});
l >>= 1, r >>= 1;
}
}
for (const auto &op : ops) {
int t = std::upper_bound(query_ts.begin(), query_ts.end(), op.first) - query_ts.begin();
nodes.at(t + D - 1).push_back(Procedure{DyConOperation::Event, op.second});
}
ret_.clear();
rec(1);
return ret_;
}
};