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=1605" #include "../matroid_intersection.hpp" #include "../matroids/graphic_matroid.hpp" #include "../matroids/partition_matroid.hpp" #include <iostream> #include <numeric> #include <utility> #include <vector> using namespace std; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); while (true) { int N, M, K; cin >> N >> M >> K; if (N == 0) break; vector<vector<int>> partition(2); vector<int> R{K, N - 1 - K}; vector<pair<int, int>> edges; vector<int> weights; for (int e = 0; e < M; ++e) { int u, v, w; char l; cin >> u >> v >> w >> l; --u, --v; partition[l == 'B'].push_back(e); edges.emplace_back(u, v); weights.push_back(w); } PartitionMatroid M1(edges.size(), partition, R); GraphicMatroid M2(N, edges); vector<bool> ret = MatroidIntersection(M1, M2, weights); int ne = accumulate(ret.begin(), ret.end(), 0); if (ne < N - 1) { cout << "-1\n"; } else { int sum = 0; for (int e = 0; e < M; ++e) sum += ret[e] * weights[e]; cout << sum << '\n'; } } }
#line 1 "combinatorial_opt/test/matroid_intersection.aoj1605.test.cpp" #define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1605" #line 2 "graph/shortest_path.hpp" #include <algorithm> #include <cassert> #include <deque> #include <fstream> #include <functional> #include <limits> #include <queue> #include <string> #include <tuple> #include <utility> #include <vector> template <typename T, T INF = std::numeric_limits<T>::max() / 2, int INVALID = -1> struct shortest_path { int V, E; bool single_positive_weight; T wmin, wmax; std::vector<std::pair<int, T>> tos; std::vector<int> head; std::vector<std::tuple<int, int, T>> edges; void build_() { if (int(tos.size()) == E and int(head.size()) == V + 1) return; tos.resize(E); head.assign(V + 1, 0); for (const auto &e : edges) ++head[std::get<0>(e) + 1]; for (int i = 0; i < V; ++i) head[i + 1] += head[i]; auto cur = head; for (const auto &e : edges) { tos[cur[std::get<0>(e)]++] = std::make_pair(std::get<1>(e), std::get<2>(e)); } } shortest_path(int V = 0) : V(V), E(0), single_positive_weight(true), wmin(0), wmax(0) {} void add_edge(int s, int t, T w) { assert(0 <= s and s < V); assert(0 <= t and t < V); edges.emplace_back(s, t, w); ++E; if (w > 0 and wmax > 0 and wmax != w) single_positive_weight = false; wmin = std::min(wmin, w); wmax = std::max(wmax, w); } void add_bi_edge(int u, int v, T w) { add_edge(u, v, w); add_edge(v, u, w); } std::vector<T> dist; std::vector<int> prev; // Dijkstra algorithm // - Requirement: wmin >= 0 // - Complexity: O(E log E) using Pque = std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>>; template <class Heap = Pque> void dijkstra(int s, int t = INVALID) { assert(0 <= s and s < V); build_(); dist.assign(V, INF); prev.assign(V, INVALID); dist[s] = 0; Heap pq; pq.emplace(0, s); while (!pq.empty()) { T d; int v; std::tie(d, v) = pq.top(); pq.pop(); if (t == v) return; if (dist[v] < d) continue; for (int e = head[v]; e < head[v + 1]; ++e) { const auto &nx = tos[e]; T dnx = d + nx.second; if (dist[nx.first] > dnx) { dist[nx.first] = dnx, prev[nx.first] = v; pq.emplace(dnx, nx.first); } } } } // Dijkstra algorithm // - Requirement: wmin >= 0 // - Complexity: O(V^2 + E) void dijkstra_vquad(int s, int t = INVALID) { assert(0 <= s and s < V); build_(); dist.assign(V, INF); prev.assign(V, INVALID); dist[s] = 0; std::vector<char> fixed(V, false); while (true) { int r = INVALID; T dr = INF; for (int i = 0; i < V; i++) { if (!fixed[i] and dist[i] < dr) r = i, dr = dist[i]; } if (r == INVALID or r == t) break; fixed[r] = true; int nxt; T dx; for (int e = head[r]; e < head[r + 1]; ++e) { std::tie(nxt, dx) = tos[e]; if (dist[nxt] > dist[r] + dx) dist[nxt] = dist[r] + dx, prev[nxt] = r; } } } // Bellman-Ford algorithm // - Requirement: no negative loop // - Complexity: O(VE) bool bellman_ford(int s, int nb_loop) { assert(0 <= s and s < V); build_(); dist.assign(V, INF), prev.assign(V, INVALID); dist[s] = 0; for (int l = 0; l < nb_loop; l++) { bool upd = false; for (int v = 0; v < V; v++) { if (dist[v] == INF) continue; for (int e = head[v]; e < head[v + 1]; ++e) { const auto &nx = tos[e]; T dnx = dist[v] + nx.second; if (dist[nx.first] > dnx) dist[nx.first] = dnx, prev[nx.first] = v, upd = true; } } if (!upd) return true; } return false; } // Bellman-ford algorithm using deque // - Requirement: no negative loop // - Complexity: O(VE) void spfa(int s) { assert(0 <= s and s < V); build_(); dist.assign(V, INF); prev.assign(V, INVALID); dist[s] = 0; std::deque<int> q; std::vector<char> in_queue(V); q.push_back(s), in_queue[s] = 1; while (!q.empty()) { int now = q.front(); q.pop_front(), in_queue[now] = 0; for (int e = head[now]; e < head[now + 1]; ++e) { const auto &nx = tos[e]; T dnx = dist[now] + nx.second; int nxt = nx.first; if (dist[nxt] > dnx) { dist[nxt] = dnx; if (!in_queue[nxt]) { if (q.size() and dnx < dist[q.front()]) { // Small label first optimization q.push_front(nxt); } else { q.push_back(nxt); } prev[nxt] = now, in_queue[nxt] = 1; } } } } } // 01-BFS // - Requirement: all weights must be 0 or w (positive constant). // - Complexity: O(V + E) void zero_one_bfs(int s, int t = INVALID) { assert(0 <= s and s < V); build_(); dist.assign(V, INF), prev.assign(V, INVALID); dist[s] = 0; std::vector<int> q(V * 4); int ql = V * 2, qr = V * 2; q[qr++] = s; while (ql < qr) { int v = q[ql++]; if (v == t) return; for (int e = head[v]; e < head[v + 1]; ++e) { const auto &nx = tos[e]; T dnx = dist[v] + nx.second; if (dist[nx.first] > dnx) { dist[nx.first] = dnx, prev[nx.first] = v; if (nx.second) { q[qr++] = nx.first; } else { q[--ql] = nx.first; } } } } } // Dial's algorithm // - Requirement: wmin >= 0 // - Complexity: O(wmax * V + E) void dial(int s, int t = INVALID) { assert(0 <= s and s < V); build_(); dist.assign(V, INF), prev.assign(V, INVALID); dist[s] = 0; std::vector<std::vector<std::pair<int, T>>> q(wmax + 1); q[0].emplace_back(s, dist[s]); int ninq = 1; int cur = 0; T dcur = 0; for (; ninq; ++cur, ++dcur) { if (cur == wmax + 1) cur = 0; while (!q[cur].empty()) { int v = q[cur].back().first; T dnow = q[cur].back().second; q[cur].pop_back(), --ninq; if (v == t) return; if (dist[v] < dnow) continue; for (int e = head[v]; e < head[v + 1]; ++e) { const auto &nx = tos[e]; T dnx = dist[v] + nx.second; if (dist[nx.first] > dnx) { dist[nx.first] = dnx, prev[nx.first] = v; int nxtcur = cur + int(nx.second); if (nxtcur >= int(q.size())) nxtcur -= q.size(); q[nxtcur].emplace_back(nx.first, dnx), ++ninq; } } } } } // Solver for DAG // - Requirement: graph is DAG // - Complexity: O(V + E) bool dag_solver(int s) { assert(0 <= s and s < V); build_(); dist.assign(V, INF), prev.assign(V, INVALID); dist[s] = 0; std::vector<int> indeg(V, 0); std::vector<int> q(V * 2); int ql = 0, qr = 0; q[qr++] = s; while (ql < qr) { int now = q[ql++]; for (int e = head[now]; e < head[now + 1]; ++e) { const auto &nx = tos[e]; ++indeg[nx.first]; if (indeg[nx.first] == 1) q[qr++] = nx.first; } } ql = qr = 0; q[qr++] = s; while (ql < qr) { int now = q[ql++]; for (int e = head[now]; e < head[now + 1]; ++e) { const auto &nx = tos[e]; --indeg[nx.first]; if (dist[nx.first] > dist[now] + nx.second) dist[nx.first] = dist[now] + nx.second, prev[nx.first] = now; if (indeg[nx.first] == 0) q[qr++] = nx.first; } } return *max_element(indeg.begin(), indeg.end()) == 0; } // Retrieve a sequence of vertex ids that represents shortest path [s, ..., goal] // If not reachable to goal, return {} std::vector<int> retrieve_path(int goal) const { assert(int(prev.size()) == V); assert(0 <= goal and goal < V); if (dist[goal] == INF) return {}; std::vector<int> ret{goal}; while (prev[goal] != INVALID) { goal = prev[goal]; ret.push_back(goal); } std::reverse(ret.begin(), ret.end()); return ret; } void solve(int s, int t = INVALID) { if (wmin >= 0) { if (single_positive_weight) { zero_one_bfs(s, t); } else if (wmax <= 10) { dial(s, t); } else { if ((long long)V * V < (E << 4)) { dijkstra_vquad(s, t); } else { dijkstra(s, t); } } } else { bellman_ford(s, V); } } // Warshall-Floyd algorithm // - Requirement: no negative loop // - Complexity: O(E + V^3) std::vector<std::vector<T>> floyd_warshall() { build_(); std::vector<std::vector<T>> dist2d(V, std::vector<T>(V, INF)); for (int i = 0; i < V; i++) { dist2d[i][i] = 0; for (const auto &e : edges) { int s = std::get<0>(e), t = std::get<1>(e); dist2d[s][t] = std::min(dist2d[s][t], std::get<2>(e)); } } for (int k = 0; k < V; k++) { for (int i = 0; i < V; i++) { if (dist2d[i][k] == INF) continue; for (int j = 0; j < V; j++) { if (dist2d[k][j] == INF) continue; dist2d[i][j] = std::min(dist2d[i][j], dist2d[i][k] + dist2d[k][j]); } } } return dist2d; } void to_dot(std::string filename = "shortest_path") const { std::ofstream ss(filename + ".DOT"); ss << "digraph{\n"; build_(); for (int i = 0; i < V; i++) { for (int e = head[i]; e < head[i + 1]; ++e) { ss << i << "->" << tos[e].first << "[label=" << tos[e].second << "];\n"; } } ss << "}\n"; ss.close(); return; } }; #line 5 "combinatorial_opt/matroid_intersection.hpp" // Find minimum weight augmenting path of matroid intersection. // m1, m2: matroids // I: independent set (will be updated if augmenting path is found) // // Return `true` iff augmenting path is found. // Complexity: O(Cn + n^2) (C: circuit query) template <class Matroid1, class Matroid2, class T = int> bool matroid_intersection_augment(Matroid1 &m1, Matroid2 &m2, std::vector<bool> &I, const std::vector<T> &weights = {}) { const int n = m1.size(); assert(m2.size() == n); assert((int)I.size() == n); auto weight = [&](int e) { return weights.empty() ? T() : weights.at(e) * (n + 1); }; const int gs = n, gt = n + 1; shortest_path<T> sssp(n + 2); m1.set(I); m2.set(I); for (int e = 0; e < n; ++e) { if (I.at(e)) continue; auto c1 = m1.circuit(e), c2 = m2.circuit(e); if (c1.empty()) sssp.add_edge(e, gt, T()); for (int f : c1) { if (f != e) sssp.add_edge(e, f, -weight(f) + T(1)); } if (c2.empty()) sssp.add_edge(gs, e, weight(e) + T(1)); for (int f : c2) { if (f != e) sssp.add_edge(f, e, weight(e) + T(1)); } } sssp.solve(gs, gt); if (auto aug_path = sssp.retrieve_path(gt); aug_path.empty()) { return false; } else { for (auto e : aug_path) { if (e != gs and e != gt) I.at(e) = !I.at(e); } return true; } } // Minimum weight matroid intersection solver // Algorithm based on http://dopal.cs.uec.ac.jp/okamotoy/lect/2015/matroid/ // Complexity: O(Cn^2 + n^3) (C : circuit query, non-weighted) template <class Matroid1, class Matroid2, class T = int> std::vector<bool> MatroidIntersection(Matroid1 matroid1, Matroid2 matroid2, std::vector<T> weights = {}) { const int n = matroid1.size(); assert(matroid2.size() == n); assert(weights.empty() or (int) weights.size() == n); std::vector<bool> I(n); if (weights.empty()) { matroid1.set(I); matroid2.set(I); for (int e = 0; e < n; ++e) { if (matroid1.circuit(e).empty() and matroid2.circuit(e).empty()) { I.at(e) = true; matroid1.set(I); matroid2.set(I); } } } while (matroid_intersection_augment(matroid1, matroid2, I, weights)) {} return I; } #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.aoj1605.test.cpp" #include <iostream> #include <numeric> #line 9 "combinatorial_opt/test/matroid_intersection.aoj1605.test.cpp" using namespace std; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); while (true) { int N, M, K; cin >> N >> M >> K; if (N == 0) break; vector<vector<int>> partition(2); vector<int> R{K, N - 1 - K}; vector<pair<int, int>> edges; vector<int> weights; for (int e = 0; e < M; ++e) { int u, v, w; char l; cin >> u >> v >> w >> l; --u, --v; partition[l == 'B'].push_back(e); edges.emplace_back(u, v); weights.push_back(w); } PartitionMatroid M1(edges.size(), partition, R); GraphicMatroid M2(N, edges); vector<bool> ret = MatroidIntersection(M1, M2, weights); int ne = accumulate(ret.begin(), ret.end(), 0); if (ne < N - 1) { cout << "-1\n"; } else { int sum = 0; for (int e = 0; e < M; ++e) sum += ret[e] * weights[e]; cout << sum << '\n'; } } }