Submission #437966

#TimeUsernameProblemLanguageResultExecution timeMemory
437966fleimgruberKeys (IOI21_keys)C++17
0 / 100
28 ms35532 KiB
// --- algolib/union-find.h --- // union find, optionally with tags for each component. this is useful // for example when compressing components. enable using template parameter #include <algorithm> #include <type_traits> #include <numeric> #include <vector> template<typename T = void, typename enable = void> class UnionFind { std::vector<int> p, size_; public: UnionFind(int n = 0) : p(n), size_(n, 1) { std::iota(p.begin(), p.end(), 0); } int create() { int r = p.size(); p.push_back(r); size_.push_back(1); return r; } int find(int i) { if (i == p[i]) return i; return p[i] = find(p[i]); } int operator[](int i) { return find(i); } int size(int i) { return size_[find(i)]; } bool connected(int a, int b) { return find(a) == find(b); } bool connect(int a, int b) { a = find(a), b = find(b); if (a == b) return false; if (size_[a] > size_[b]) std::swap(a, b); size_[b] += size_[a]; p[a] = b; return true; } }; template<typename T> class UnionFind<T, typename std::enable_if_t<!std::is_same_v<T, void>>> : public UnionFind<void> { std::vector<T> tags; public: UnionFind(int n = 0) : UnionFind<void>(n), tags(n) { } void set_tag(int i, const T& t) { tags[find(i)] = t; } const T& tag(int i) { return tags[find(i)]; } }; // ---------------- #include <bits/stdc++.h> using namespace std; const int MAX_N = 300005; int n; vector<int> ans; struct Edge { int to, key; // key required }; vector<Edge> out[MAX_N]; // outgoing edges. TODO optimise int vis[MAX_N], par[MAX_N]; // vis_time, parent in DAG/tree set<int> keys[MAX_N]; std::vector<int> in_comp[MAX_N]; // for each vertex the set of vertices // it represents // weakly is weakly connected components UnionFind<int> compressed(MAX_N), weakly(MAX_N); // merge i into j // TODO smaller-to-larger void merge(int i, int j) { compressed.connect(i, j); compressed.set_tag(j, j); in_comp[j].insert(in_comp[j].end(), in_comp[i].begin(), in_comp[i].end()); in_comp[i].clear(); for (int x : keys[i]) keys[j].insert(x); keys[i].clear(); // TODO no duplicates out[j].insert(out[j].end(), out[i].begin(), out[i].end()); out[i].clear(); } void solve() { bool trivial = false; for (int i = 0; i < n; i++) { bool found_out = false; for (auto& e : out[i]) if (e.key == *keys[i].begin()) { // keys has size 1 found_out = true; par[i] = e.to; weakly.connect(i, e.to); break; } if (!found_out) { trivial = true; ans[i] = 1; } } if (trivial) return; // otherwise each weakly connected component is a tree // with one extra edge. compress corresponding cycle to get a // directed forest // tags are the roots (where keys & edges are stored) for (int i = 0; i < n; i++) { compressed.set_tag(i, i); in_comp[i].push_back(i); } // roots, ordered by size struct PQKey { int vertex, size; bool operator<(const PQKey& o) const { return size > o.size; } }; priority_queue<PQKey> roots; for (int i = 0; i < n; i++) if (vis[i] == 0) { int t = i + 1; // 1-based vis times int j = i; do { vis[j] = t; j = par[j]; } while (vis[j] == 0); if (vis[j] == t) { // cycle! int start = j; for (j = par[j]; j != start; j = par[j]) merge(j, start); par[start] = -1; roots.push({ start, compressed.size(start) }); } } // != -1 means we already found an answer of size done_size // so stop pushing to pq, but collect remaining components int done_size = -1; while (!roots.empty()) { auto [r, _] = roots.top(); roots.pop(); // find any outgoing edge bool has_outgoing = false; for (auto& e : out[r]) { // TODO skip useless edges/duplicates if (compressed.connected(r, e.to) || keys[r].find(e.key) == keys[r].end()) continue; if (weakly.connected(r, e.to)) { // merge with self // walk up parent chain, until we're in root comp int j = compressed.tag(e.to); do { int jp = compressed.tag(par[j]); merge(j, jp); j = jp; } while (par[j] != -1); // we're still a root, but of larger size roots.push({ r, compressed.size(r) }); } else { // other component has_outgoing = true; // merge with other component, if not done if (done_size != -1) break; has_outgoing = true; par[r] = e.to; weakly.connect(r, e.to); // nothing new for priority queue, as we're // not a root anymore break; } } if (!has_outgoing && (done_size == -1 || done_size == compressed.size(r))) { done_size = compressed.size(r); for (int x : in_comp[r]) ans[x] = 1; } } } vector<int> find_reachable(vector<int> r, vector<int> u, vector<int> v, vector<int> c) { n = r.size(); for (int i = 0; i < n; i++) keys[i].insert(r[i]); int m = u.size(); for (int i = 0; i < m; i++) { out[u[i]].push_back({ v[i], c[i] }); out[v[i]].push_back({ u[i], c[i] }); } ans.assign(n, 0); solve(); return ans; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...