Submission #685355

#TimeUsernameProblemLanguageResultExecution timeMemory
685355lunchbox1Dango Maker (JOI18_dango_maker)C++17
33 / 100
378 ms262144 KiB
// #include <algorithm> // #include <array> // #include <iostream> // #include <vector> // #include <queue> // #include <numeric> #include <bits/stdc++.h> using namespace std; const int N = 3000; #define assert(...) 0 // https://judge.yosupo.jp/submission/110301 struct BipartiteGraph{ // https://judge.yosupo.jp/submission/59969 // Max matching in O(E root(V)) --> considerably fast, runs under 100ms for E, V <= 2e5 on library checker private: int n; int Lpart, Rpart; int matching_num; // std::vector<std::vector<int>> G; vector<array<int, 2>> raw_edges; vector<int> num_starts, final_edges; public: std::vector<int> match; BipartiteGraph(int a, int b, int p = 0): Lpart(a), Rpart(b), n(a + b), matching_num(-1), num_starts(a + b + 1, 0), match(n, -1) { raw_edges.reserve(p); } BipartiteGraph(int a, int b, const vector<array<int, 2>>& Edges): Lpart(a), Rpart(b), n(a + b), matching_num(-1), raw_edges(Edges), num_starts(a + b + 1, 0), match(n, -1) {} inline void addEdge(int u, int v){ raw_edges.push_back({u, v});} inline void build() { for(const auto& [u, v]: raw_edges) { ++num_starts[u + 1]; ++num_starts[v + Lpart + 1]; } for(int i = 1; i <= n; ++i) num_starts[i] += num_starts[i - 1]; final_edges.resize(num_starts[n]); vector<int> start(num_starts.begin(), num_starts.begin() + n); for(const auto& [u, v]: raw_edges){ final_edges[start[u]++] = v + Lpart; final_edges[start[v + Lpart]++] = u; } } int maxMatching(){ if (final_edges.size() != raw_edges.size()) build(); int res = 0; bool update = true; std::vector<int> pre(Lpart, -1), root(Lpart, -1); while(update){ update = false; std::queue<int> que; for(int i = 0; i < Lpart; i++){ if(!~match[i]){ root[i] = i; que.push(i); } } while(que.size()){ int v = que.front(); que.pop(); if(~match[root[v]]) continue; for(int cur = num_starts[v]; cur < num_starts[v + 1]; ++cur){ int nv = final_edges[cur]; if(!~match[nv]){ while(~nv){ match[nv] = v; std::swap(match[v], nv); v = pre[v]; } update = true; ++res; break; } nv = match[nv]; if(~pre[nv]) continue; pre[nv] = v, root[nv] = root[v]; que.push(nv); } } if(update){ std::fill(pre.begin(), pre.end(), -1); std::fill(root.begin(), root.end(), -1); } } return matching_num = res; } std::vector<std::array<int, 2>> maxMatchingEdges() { if (matching_num == -1) maxMatching(); std::vector<std::array<int, 2>> ans; ans.reserve(matching_num); for(int i = 0; i < Lpart; ++i){ if (match[i] != -1) ans.push_back({i, match[i] - Lpart}); } return ans; } inline int pair(int u) const {return match[u];} std::vector<std::array<int, 2>> minimumEdgeCover(){ // Minimum edges to cover each vertex atleast once. if (matching_num == -1) maxMatching(); int idx = 0; std::vector<std::array<int, 2>> res(n - matching_num); for(int i = 0; i < Lpart; i++){ if(match[i] != -1) res[idx++] = {i, match[i]}; else res[idx++] = {i, final_edges[num_starts[i + 1] - 1]}; } assert(n - matching_num == idx); return res; } std::pair<std::vector<int>, std::vector<int>> minimumVertexCover(){ if (matching_num == -1) maxMatching(); std::vector<bool> reachable(n); for(int i = 0; i < Lpart; i++){ if(match[i] != -1) continue; std::queue<int> que; que.push(i); while(que.size()){ int v = que.front(); que.pop(); reachable[v] = true; for(int cur = num_starts[v]; cur < num_starts[v + 1]; ++cur){ const int nv = final_edges[cur]; if(reachable[nv]) continue; if(v >= Lpart && match[v] == nv){ reachable[nv] = true; que.push(nv); } if(v < Lpart && match[v] != nv){ reachable[nv] = true; que.push(nv); } } } } std::vector<int> left, right; for(int i = 0; i < n; i++){ if(i < Lpart && !reachable[i]) left.emplace_back(i); if(i >= Lpart && reachable[i]) right.emplace_back(i); } return std::make_pair(left, right); } std::pair<std::vector<int>, std::vector<int>> maxIndependentSet(){ if (matching_num == -1) maxMatching(); std::vector<int> left, right; auto p = minimumVertexCover(); std::vector<bool> complement(n, false); for(const int &v : p.first) complement[v] = true; for(const int &v : p.second) complement[v] = true; for(int i = 0; i < n; i++){ if(complement[i]) continue; if(i < Lpart) right.emplace_back(i); else left.emplace_back(i); } return std::make_pair(left, right); } }; struct BipartiteEdgeColoring { struct UnionFind { vector<int> UF; UnionFind(int N) : UF(N, -1) {} inline int find(int v) { return UF[v] < 0 ? v : UF[v] = find(UF[v]); } inline void join(int v, int w) { if ((v = find(v)) == (w = find(w))) return; if (UF[v] > UF[w]) swap(v, w); UF[v] += UF[w]; UF[w] = v; return;} }; struct Pair { int s, v; Pair(int s, int v) : s(s), v(v) {} bool operator < (const Pair &o) const { return s > o.s; } }; int makeDRegular(int &V, vector<pair<int, int>> &edges, int &L) { // O(V log V + E), adds atmost E + D new edges vector<int> deg(V, 0); for (auto &&e : edges) { deg[e.first]++; deg[e.second]++; } int D = *max_element(deg.begin(), deg.end()); UnionFind uf(V); vector<int> cnt(2, 0); int R = V - L; for (int s = 0; s < 2; s++) { std::priority_queue<Pair> PQ; for(int v = s * L; v < L + s * R; ++v) { PQ.push({deg[v], v}); } cnt[s] = PQ.size(); while (int(PQ.size()) >= 2) { Pair a = PQ.top(); PQ.pop(); Pair b = PQ.top(); PQ.pop(); if (a.s + b.s <= D) { uf.join(a.v, b.v); PQ.emplace(a.s + b.s, a.v); --cnt[s];} else break; } } vector<int> id(V, -1); if (cnt[0] >= cnt[1]) { int curId = 0; for(int v = 0; v < V; ++v) if (uf.find(v) == v) id[v] = curId++; } else{ int curId = 0; for(int v = V - 1; v >= 0; --v) if (uf.find(v) == v) id[v] = curId++; for(auto &&e: edges){ swap(e.first, e.second); } swap(cnt[0], cnt[1]); } assert (cnt[0] >= cnt[1]); deg.assign(V = cnt[0] * 2, 0); edges.reserve(V * D / 2); for (auto &&e : edges) { deg[e.first = id[uf.find(e.first)]]++; deg[e.second = id[uf.find(e.second)]]++; assert (e.first < e.second); } for(int v = 0, w = cnt[0]; v < cnt[0]; ++v) { while (deg[v] < D){ while (w < V && deg[w] == D) ++w; int x = min(D - deg[w], D - deg[v]); for(int k = 0; k < x; ++k){ edges.emplace_back(v, w); } deg[v] += x; deg[w] += x; } } L = cnt[0]; return D; } vector<int> eulerianCircuit( int V, const vector<pair<int, int>> &edges, const vector<int> &inds) { vector<vector<pair<int, int>>> G(V); vector<int> circuit; for (int i = 0; i < int(inds.size()); i++) { int v, w; tie(v, w) = edges[inds[i]]; G[v].emplace_back(w, i); G[w].emplace_back(v, i); } vector<bool> vis1(V, false), vis2(inds.size(), false); vector<pair<int, int>> stk; for (int s = 0; s < V; s++) if (!vis1[s]) { stk.clear(); stk.emplace_back(s, -1); while (!stk.empty()) { int v, w, e; tie(v, e) = stk.back(); vis1[v] = true; if (G[v].empty()) { circuit.emplace_back(e); stk.pop_back(); } else { tie(w, e) = G[v].back(); G[v].pop_back(); if (!vis2[e]) { vis2[e] = true; stk.emplace_back(w, e); } } } circuit.pop_back(); } for (auto &&e : circuit) e = inds[e]; return circuit; } vector<int> color; // static vector<int> maxMatchDRegular(int V, vector<pair<int, int>> edges, int D, int K) { // const int E = edges.size(); // const int L = (V >> 1); // assert (E == ((V * 1LL * D) / 2) && K <= D && (L << 1) == V); // vector<int> color(E, -1); // vector<vector<int>> G(V); // vector<int> xv(E); // for(int i = 0; i < E; ++i) { // int u, v; tie(u, v) = edges[i]; // G[u].push_back(i); // xv[i] = u ^ v; // } // vector<int> match(V, -1); // vector<int> path; // function<bool(int, int)> augment = [&] (int u, int b) { // if (u > L) { // if (match[u] == -1){ // return true; // } // u = edges[match[u]].second; // path.push_back(match[u]); // int k = G // } // else{ // int v = G[u].size(); // int x = random_long(0, v - 1); // path.push_back() // } // }; // for(int i = 0; i < K; ++i) { // } // } BipartiteEdgeColoring(int L, int R, vector<pair<int, int>> edges) // O(E sqrt V log (D)) : color(edges.size(), -1) { int V = L + R; for (auto &&e : edges) { assert(e.first < L && e.second < R); e.second += L; } int D = makeDRegular(V, edges, L), curCol = 0; for(auto &&e: edges){ assert (e.first < L && e.second >= L); } R = L; function<void(int, const vector<int> &)> rec = [&] ( int d, const vector<int> &inds) { if (d == 0) return; else if (d == 1) { for (int e : inds) if (e < int(color.size())) color[e] = curCol; curCol++; } else if (d % 2 == 0) { vector<int> circuit = eulerianCircuit(V, edges, inds), half1, half2; half1.reserve(circuit.size() / 2); half2.reserve(circuit.size() / 2); for (int i = 0; i < int(circuit.size()); i += 2) { half1.push_back(circuit[i]); half2.push_back(circuit[i + 1]); } rec(d / 2, half1); rec(d / 2, half2); } else { vector<vector<int>> G(V); BipartiteGraph mm(L, L); for (int e : inds) { int v, w; tie(v, w) = edges[e]; mm.addEdge(v, w - L); } mm.maxMatching(); vector<int> unmatched; for (int e : inds) { int v, w; tie(v, w) = edges[e]; if (mm.match[v] == w) { mm.match[v] = -1; mm.match[w] = -1; if (e < int(color.size())) color[e] = curCol; } else unmatched.push_back(e); } curCol++; rec(d - 1, unmatched); } }; vector<int> inds(edges.size()); iota(inds.begin(), inds.end(), 0); rec(D, inds); } }; int n, m; string s[N]; bool t1(int i, int j) { return i > 0 && i < n - 1 && s[i - 1][j] == 'R' && s[i][j] == 'G' && s[i + 1][j] == 'W'; } bool t2(int i, int j) { return j > 0 && j < m - 1 && s[i][j - 1] == 'R' && s[i][j] == 'G' && s[i][j + 1] == 'W'; } int iu[N][N], iv[N][N], u = 0, v = 0; vector<pair<int, int>> edge; void link_(int i1, int j1, int i2, int j2) { if (i1 < 0 || i1 >= n || i2 < 0 || i2 >= n || j1 < 0 || j1 >= m || j2 < 0 || j2 >= m) return; if (iu[i1][j1] != -1 && iv[i2][j2] != -1) { // cout << i1 << ' ' << j1 << " -> " << i2 << ' ' << j2 << '\n'; edge.push_back({iu[i1][j1], iv[i2][j2]}); } } int main() { ios::sync_with_stdio(0), cin.tie(0); cin >> n >> m; for (int i = 0; i < n; i++) cin >> s[i]; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) { iu[i][j] = t1(i, j) ? u++ : -1; iv[i][j] = t2(i, j) ? v++ : -1; } // dinic::build(u + v + 2); // int s = u + v, t = s + 1; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) { if (iu[i][j] != -1) { // cout << "U " << i << ' ' << j << '\n'; // dinic::link(s, iu[i][j], 1); // link_(i, j, i - 1, j + 1); // link_(i, j, i + 1, j - 1); } if (iv[i][j] != -1) { // cout << "V " << i << ' ' << j << '\n'; // dinic::link(u + iv[i][j], t, 1); link_(i + 1, j - 1, i, j); link_(i - 1, j + 1, i, j); } link_(i, j, i, j); } BipartiteGraph g(u, v, edge.size()); for (auto [u, v] : edge) g.addEdge(u, v); // HopcroftKarp hk(u, v, edge); cout << u + v - g.maxMatchingEdges().size() << '\n'; return 0; }

Compilation message (stderr)

dango_maker.cpp:12: warning: "assert" redefined
   12 | #define assert(...) 0
      | 
In file included from /usr/include/c++/10/cassert:44,
                 from /usr/include/x86_64-linux-gnu/c++/10/bits/stdc++.h:33,
                 from dango_maker.cpp:7:
/usr/include/assert.h:92: note: this is the location of the previous definition
   92 | #  define assert(expr)       \
      | 
dango_maker.cpp: In constructor 'BipartiteGraph::BipartiteGraph(int, int, int)':
dango_maker.cpp:20:16: warning: 'BipartiteGraph::Rpart' will be initialized after [-Wreorder]
   20 |     int Lpart, Rpart;
      |                ^~~~~
dango_maker.cpp:19:9: warning:   'int BipartiteGraph::n' [-Wreorder]
   19 |     int n;
      |         ^
dango_maker.cpp:29:5: warning:   when initialized here [-Wreorder]
   29 |     BipartiteGraph(int a, int b, int p = 0): Lpart(a), Rpart(b), n(a + b), matching_num(-1), num_starts(a + b + 1, 0), match(n, -1) {
      |     ^~~~~~~~~~~~~~
dango_maker.cpp: In constructor 'BipartiteGraph::BipartiteGraph(int, int, const std::vector<std::array<int, 2> >&)':
dango_maker.cpp:20:16: warning: 'BipartiteGraph::Rpart' will be initialized after [-Wreorder]
   20 |     int Lpart, Rpart;
      |                ^~~~~
dango_maker.cpp:19:9: warning:   'int BipartiteGraph::n' [-Wreorder]
   19 |     int n;
      |         ^
dango_maker.cpp:32:5: warning:   when initialized here [-Wreorder]
   32 |     BipartiteGraph(int a, int b, const vector<array<int, 2>>& Edges): Lpart(a), Rpart(b), n(a + b), matching_num(-1), raw_edges(Edges), num_starts(a + b + 1, 0), match(n, -1) {}
      |     ^~~~~~~~~~~~~~
dango_maker.cpp: In member function 'std::vector<std::array<int, 2> > BipartiteGraph::minimumEdgeCover()':
dango_maker.cpp:12:21: warning: statement has no effect [-Wunused-value]
   12 | #define assert(...) 0
      |                     ^
dango_maker.cpp:113:9: note: in expansion of macro 'assert'
  113 |         assert(n - matching_num == idx);
      |         ^~~~~~
dango_maker.cpp: In member function 'int BipartiteEdgeColoring::makeDRegular(int&, std::vector<std::pair<int, int> >&, int&)':
dango_maker.cpp:12:21: warning: statement has no effect [-Wunused-value]
   12 | #define assert(...) 0
      |                     ^
dango_maker.cpp:222:5: note: in expansion of macro 'assert'
  222 |     assert (cnt[0] >= cnt[1]);
      |     ^~~~~~
dango_maker.cpp:12:21: warning: statement has no effect [-Wunused-value]
   12 | #define assert(...) 0
      |                     ^
dango_maker.cpp:228:7: note: in expansion of macro 'assert'
  228 |       assert (e.first < e.second);
      |       ^~~~~~
dango_maker.cpp: In constructor 'BipartiteEdgeColoring::BipartiteEdgeColoring(int, int, std::vector<std::pair<int, int> >)':
dango_maker.cpp:12:21: warning: statement has no effect [-Wunused-value]
   12 | #define assert(...) 0
      |                     ^
dango_maker.cpp:314:7: note: in expansion of macro 'assert'
  314 |       assert(e.first < L && e.second < R);
      |       ^~~~~~
dango_maker.cpp:12:21: warning: statement has no effect [-Wunused-value]
   12 | #define assert(...) 0
      |                     ^
dango_maker.cpp:321:7: note: in expansion of macro 'assert'
  321 |       assert (e.first < L && e.second >= L);
      |       ^~~~~~
dango_maker.cpp:320:16: warning: unused variable 'e' [-Wunused-variable]
  320 |     for(auto &&e: edges){
      |                ^
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...