This documentation is automatically generated by online-judge-tools/verification-helper
View the Project on GitHub hitonanode/cplib-cpp
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_2_B" #include "../matroid_intersection_dijkstra.hpp" #include "../matroids/graphic_matroid.hpp" #include "../matroids/partition_matroid.hpp" #include <iostream> #include <utility> #include <vector> using namespace std; int main() { int V, E, r; cin >> V >> E >> r; vector<vector<int>> partition(V); vector<int> R(V, 1); R.at(r) = 0; vector<pair<int, int>> edges; vector<int> weight; for (int e = 0; e < E; ++e) { int s, t, w; cin >> s >> t >> w; partition.at(t).push_back(e); edges.emplace_back(s, t); weight.emplace_back(-w); } PartitionMatroid M1(E, partition, R); GraphicMatroid M2(V, edges); vector<int> potential(weight.size()); vector<bool> sol(weight.size()); while (augment_matroid_intersection_dijkstra(M1, M2, sol, weight, potential)) continue; int ret = 0; for (int e = 0; e < E; ++e) ret -= sol.at(e) * weight.at(e); cout << ret << '\n'; }
#line 1 "combinatorial_opt/test/matroid_intersection_dijkstra.aoj_grl_2_b.test.cpp" #define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_2_B" #line 2 "combinatorial_opt/matroid_intersection_dijkstra.hpp" #include <cassert> #include <queue> #include <vector> // Find maximum weight size k + 1 intersection of two matroids using Dijkstra's algorithm // Return `true` iff larger intersection is found. // Complexity: O(Cn + nk log n) (C: circuit query) template <class Matroid1, class Matroid2, class T = int> bool augment_matroid_intersection_dijkstra( Matroid1 &m1, // Matroid, size n, updated Matroid2 &m2, // Matroid, size n, updated std::vector<bool> &I, // Size k maximum weight intersection, size n, updated const std::vector<T> &weight, // Weights of elements, size n std::vector<T> &potential // Potential, size n + 2, updated ) { const int n = I.size(); assert((int)m1.size() == n); assert((int)m2.size() == n); assert((int)weight.size() == n); assert(potential.empty() or ((int)potential.size() == n) or ((int)potential.size() == n + 2)); m1.set(I); m2.set(I); potential.resize(n + 2); auto l = [&](int e) -> T { return e < n ? (I.at(e) ? weight.at(e) : -weight.at(e)) : T(); }; auto edge_len = [&](int s, int t) -> T { return l(t) - potential.at(t) + potential.at(s); }; if (true) { // 自明な追加が可能かチェック(省略してもアルゴリズムは正当) int max_elem = -1; for (int e = 0; e < n; ++e) { if (!I.at(e) and (max_elem < 0 or weight.at(max_elem) < weight.at(e))) max_elem = e; } if (max_elem < 0) return false; for (int e = 0; e < n; ++e) { if (!I.at(e) and weight.at(e) == weight.at(max_elem) and m1.circuit(e).empty() and m2.circuit(e).empty()) { potential.at(e) -= l(e); I.at(e) = true; return true; } } } // Find minimum length (& minimum num. of vertices) gs-gt path const int gs = n, gt = n + 1; std::vector<std::vector<int>> to(gt + 1); bool has_gs_edge = false, has_gt_edge = false; for (int e = 0; e < n; ++e) { if (I.at(e)) continue; const auto c1 = m1.circuit(e), c2 = m2.circuit(e); if (c1.empty()) { to.at(e).push_back(gt); if (!has_gt_edge) { has_gt_edge = true; potential.at(gt) = potential.at(e); } if (T el = edge_len(e, gt); el < T()) potential.at(gt) += el; } for (int f : c1) { if (f != e) to.at(e).push_back(f); } if (c2.empty()) { to.at(gs).push_back(e); if (!has_gs_edge) { has_gs_edge = true; potential.at(gs) = potential.at(e) - l(e); } if (T el = edge_len(gs, e); el < T()) potential.at(gs) -= el; } for (int f : c2) { if (f != e) to.at(f).push_back(e); } } if (const T e0 = potential.at(gs); e0 != T()) { for (auto &p : potential) p -= e0; } if (!has_gs_edge or !has_gt_edge) return false; std::vector<bool> potential_fixed(gt + 1); T potential_add_unfixed_es = T(); auto fix_potential = [&](int e) -> void { assert(!potential_fixed.at(e)); potential_fixed.at(e) = true; potential.at(e) += potential_add_unfixed_es; }; std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<>> pq; std::vector<T> dijkstra(gt + 1); std::vector<int> prv(gt + 1, -1); pq.emplace(T(), gs); while (!pq.empty()) { const int e = pq.top().second; pq.pop(); if (potential_fixed.at(e)) continue; if (e != gs) potential_add_unfixed_es = edge_len(prv.at(e), e); std::vector<std::pair<int, int>> push_cands; auto rec = [&](auto &&self, int cur) -> bool { if (cur == gt) return true; fix_potential(cur); for (int nxt : to.at(cur)) { if (potential_fixed.at(nxt)) continue; const T len = edge_len(cur, nxt) - potential_add_unfixed_es; // if (len < T()) std::cerr << cur << ' ' << nxt << ' ' << len << std::endl; assert(len >= T()); if (len == T()) { prv.at(nxt) = cur; if (self(self, nxt)) return true; } else { if (prv.at(nxt) == -1 or potential_add_unfixed_es + len < dijkstra.at(nxt)) { dijkstra.at(nxt) = potential_add_unfixed_es + len; prv.at(nxt) = cur; push_cands.emplace_back(nxt, cur); } } } return false; }; if (rec(rec, e)) break; for (auto [nxt, now] : push_cands) { if (prv.at(nxt) == now) pq.emplace(dijkstra.at(nxt), nxt); } } for (int e = 0; e < gt + 1; ++e) { if (!potential_fixed.at(e)) fix_potential(e); } if (prv.at(gt) < 0) return false; prv.assign(gt + 1, -1); std::queue<int> q; q.push(gs); for (int now = q.front(); now != gt; now = q.front()) { q.pop(); for (int nxt : to.at(now)) { if (prv.at(nxt) == -1 and edge_len(now, nxt) == T()) { prv.at(nxt) = now; q.push(nxt); } } } for (int e = prv.at(gt); e != gs; e = prv.at(e)) { potential.at(e) -= l(e); I.at(e) = !I.at(e); } return true; } #line 3 "combinatorial_opt/matroids/graphic_matroid.hpp" #include <utility> #line 5 "combinatorial_opt/matroids/graphic_matroid.hpp" // GraphicMatroid: subgraph of undirected graphs, without loops class GraphicMatroid { using Vertex = int; using Element = int; int M; int V; // # of vertices of graph std::vector<std::vector<std::pair<Vertex, Element>>> to; std::vector<std::pair<Vertex, Vertex>> edges; std::vector<Element> backtrack; std::vector<Vertex> vprev; std::vector<int> depth, root; public: GraphicMatroid(int V, const std::vector<std::pair<Vertex, Vertex>> &edges_) : M(edges_.size()), V(V), to(V), edges(edges_) { for (int e = 0; e < int(edges_.size()); e++) { int u = edges_[e].first, v = edges_[e].second; assert(0 <= u and u < V); assert(0 <= v and v < V); if (u != v) { to[u].emplace_back(v, e); to[v].emplace_back(u, e); } } } int size() const { return M; } std::vector<Vertex> que; template <class State> void set(State I) { assert(int(I.size()) == M); backtrack.assign(V, -1); vprev.assign(V, -1); depth.assign(V, -1); root.assign(V, -1); que.resize(V); int qb = 0, qe = 0; for (Vertex i = 0; i < V; i++) { if (backtrack[i] >= 0) continue; que[qb = 0] = i, qe = 1, depth[i] = 0; while (qb < qe) { Vertex now = que[qb++]; root[now] = i; for (auto nxt : to[now]) { if (depth[nxt.first] < 0 and I[nxt.second]) { backtrack[nxt.first] = nxt.second; vprev[nxt.first] = now; depth[nxt.first] = depth[now] + 1; que[qe++] = nxt.first; } } } } } std::vector<Element> circuit(const Element e) const { assert(0 <= e and e < M); Vertex s = edges[e].first, t = edges[e].second; if (root[s] != root[t]) return {}; std::vector<Element> ret{e}; auto step = [&](Vertex &i) { ret.push_back(backtrack[i]), i = vprev[i]; }; int ddepth = depth[s] - depth[t]; for (; ddepth > 0; --ddepth) step(s); for (; ddepth < 0; ++ddepth) step(t); while (s != t) step(s), step(t); return ret; } }; #line 4 "combinatorial_opt/matroids/partition_matroid.hpp" // Partition matroid (partitional matroid) : direct sum of uniform matroids class PartitionMatroid { using Element = int; int M; std::vector<std::vector<Element>> parts; std::vector<int> belong; std::vector<int> R; std::vector<int> cnt; std::vector<std::vector<Element>> circuits; public: // parts: partition of [0, 1, ..., M - 1] // R: only R[i] elements from parts[i] can be chosen for each i. PartitionMatroid(int M, const std::vector<std::vector<int>> &parts_, const std::vector<int> &R_) : M(M), parts(parts_), belong(M, -1), R(R_) { assert(parts.size() == R.size()); for (int i = 0; i < int(parts.size()); i++) { for (Element e : parts[i]) belong[e] = i; } for (Element e = 0; e < M; e++) { // assert(belong[e] != -1); if (belong[e] == -1) { belong[e] = parts.size(); parts.push_back({e}); R.push_back(1); } } } int size() const { return M; } template <class State> void set(const State &I) { cnt = R; for (int e = 0; e < M; e++) { if (I[e]) cnt[belong[e]]--; } circuits.assign(cnt.size(), {}); for (int e = 0; e < M; e++) { if (I[e] and cnt[belong[e]] == 0) circuits[belong[e]].push_back(e); } } std::vector<Element> circuit(const Element e) const { assert(0 <= e and e < M); int p = belong[e]; if (cnt[p] == 0) { auto ret = circuits[p]; ret.push_back(e); return ret; } return {}; } }; #line 5 "combinatorial_opt/test/matroid_intersection_dijkstra.aoj_grl_2_b.test.cpp" #include <iostream> #line 8 "combinatorial_opt/test/matroid_intersection_dijkstra.aoj_grl_2_b.test.cpp" using namespace std; int main() { int V, E, r; cin >> V >> E >> r; vector<vector<int>> partition(V); vector<int> R(V, 1); R.at(r) = 0; vector<pair<int, int>> edges; vector<int> weight; for (int e = 0; e < E; ++e) { int s, t, w; cin >> s >> t >> w; partition.at(t).push_back(e); edges.emplace_back(s, t); weight.emplace_back(-w); } PartitionMatroid M1(E, partition, R); GraphicMatroid M2(V, edges); vector<int> potential(weight.size()); vector<bool> sol(weight.size()); while (augment_matroid_intersection_dijkstra(M1, M2, sol, weight, potential)) continue; int ret = 0; for (int e = 0; e < E; ++e) ret -= sol.at(e) * weight.at(e); cout << ret << '\n'; }