제출 #1343412

#제출 시각아이디문제언어결과실행 시간메모리
1343412madamadam3Simurgh (IOI17_simurgh)C++20
30 / 100
78 ms2884 KiB
#include "simurgh.h"
#include <bits/stdc++.h>
using namespace std;

// Extremely minimal DSU
struct DSU {
    vector<int> par;
    DSU(int n) { par.assign(n, -1); }
    int find(int v) { return par[v] < 0 ? v : par[v] = find(par[v]); }
    bool unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return false;
        par[b] = a;
        return true;
    }
};

vector<int> find_roads(int n, vector<int> u, vector<int> v) {
    int m = u.size();
    vector<vector<int>> adj(n);
    for (int i = 0; i < m; ++i) {
        adj[u[i]].push_back(i);
        adj[v[i]].push_back(i);
    }

    vector<int> state(m, -1);      // -1: unknown, 0: not royal, 1: royal
    vector<bool> is_tree(m, false);
    vector<int> depth(n, -1), par_node(n, -1), par_edge(n, -1), tree_edges;

    // 1. Build a basic DFS Spanning Tree
    auto dfs = [&](auto& self, int cur, int p, int d) -> void {
        depth[cur] = d;
        for (int id : adj[cur]) {
            int nxt = u[id] ^ v[id] ^ cur; // Fast way to get the other endpoint
            if (nxt == p) continue;
            if (depth[nxt] == -1) {
                is_tree[id] = true;
                par_node[nxt] = cur;
                par_edge[nxt] = id;
                tree_edges.push_back(id);
                self(self, nxt, cur, d + 1);
            }
        }
    };
    dfs(dfs, 0, -1, 0);

    // 2. PHASE 1: Resolve all cycles formed by back-edges
    for (int i = 0; i < m; ++i) {
        if (is_tree[i]) continue; // Only process back-edges

        // Find the simple path in the tree going up from descendant to ancestor
        int curr = depth[u[i]] > depth[v[i]] ? u[i] : v[i];
        int anc = depth[u[i]] > depth[v[i]] ? v[i] : u[i];
        
        vector<int> cyc = {i};
        while (curr != anc) {
            cyc.push_back(par_edge[curr]);
            curr = par_node[curr];
        }

        // Check if we need to query this cycle (do we have unknown edges?)
        int known_idx = -1;
        bool needs_query = false;
        for (int j = 0; j < cyc.size(); ++j) {
            if (state[cyc[j]] != -1) known_idx = j;
            else needs_query = true;
        }
        if (!needs_query) continue;

        // Query the spanning tree by swapping each cycle edge with the back-edge
        vector<int> ans(cyc.size(), -1);
        int mx = -1;
        for (int j = 0; j < cyc.size(); ++j) {
            if (known_idx != -1 && state[cyc[j]] != -1 && j != known_idx) continue;
            
            vector<int> q;
            for (int e : tree_edges) if (e != cyc[j]) q.push_back(e);
            if (j > 0) q.push_back(i); // j == 0 is the back-edge itself
            
            ans[j] = count_common_roads(q);
            mx = max(mx, ans[j]);
        }

        // Deduce states based on the query results
        for (int j = 0; j < cyc.size(); ++j) {
            if (state[cyc[j]] != -1) continue;
            if (known_idx == -1) {
                state[cyc[j]] = (ans[j] != mx);
            } else {
                if (ans[j] == ans[known_idx]) state[cyc[j]] = state[cyc[known_idx]];
                else if (ans[j] > ans[known_idx]) state[cyc[j]] = 0;
                else state[cyc[j]] = 1;
            }
        }
    }

    // Any tree edge still unknown was never in a cycle (it's a bridge). Therefore, royal.
    for (int e : tree_edges) if (state[e] == -1) state[e] = 1;

    // 3. PHASE 2: Divide and Conquer the remaining graph
    auto query_subset = [&](const vector<int>& subset) {
        DSU dsu(n);
        vector<int> q;
        int known_royals = 0;
        for (int e : subset) {
            q.push_back(e);
            dsu.unite(u[e], v[e]);
        }
        for (int e : tree_edges) { // Complete the tree using known tree edges
            if (dsu.unite(u[e], v[e])) {
                q.push_back(e);
                known_royals += state[e];
            }
        }
        return count_common_roads(q) - known_royals;
    };

    auto solve_star = [&](auto& self, vector<int> edges, int expected) -> void {
        if (expected == 0) {
            for (int e : edges) state[e] = 0; return;
        }
        if (expected == (int)edges.size()) {
            for (int e : edges) state[e] = 1; return;
        }
        
        int mid = edges.size() / 2;
        vector<int> L(edges.begin(), edges.begin() + mid);
        vector<int> R(edges.begin() + mid, edges.end());
        
        int left_count = query_subset(L);
        self(self, L, left_count);
        self(self, R, expected - left_count);
    };

    // Run the D&C on all unknown edges incident to each vertex
    for (int i = 0; i < n; ++i) {
        vector<int> unk;
        for (int id : adj[i]) if (state[id] == -1) unk.push_back(id);
        if (!unk.empty()) solve_star(solve_star, unk, query_subset(unk));
    }

    // 4. Compile answers
    vector<int> royal_roads;
    for (int i = 0; i < m; ++i) if (state[i] == 1) royal_roads.push_back(i);
    return royal_roads;
}
#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...