제출 #1336403

#제출 시각아이디문제언어결과실행 시간메모리
1336403rockstarSopsug (EGOI23_sopsug)C++20
100 / 100
283 ms57492 KiB
#include <bits/stdc++.h>
using namespace std;

int main(){
    int N, M, K;
    scanf("%d%d%d", &N, &M, &K);

    vector<int> par(N, -1);
    vector<vector<int>> ch(N);
    vector<pair<int,int>> gedges;

    for(int i = 0; i < M; i++){
        int a, b;
        scanf("%d%d", &a, &b);
        gedges.push_back({a, b});
        // Each node can have at most one outgoing good edge
        if(par[a] != -1){
            puts("NO");
            return 0;
        }
        par[a] = b;
        ch[b].push_back(a);
    }

    // Check acyclicity with union-find
    vector<int> uf(N);
    iota(uf.begin(), uf.end(), 0);
    auto find = [&](int x){
        while(uf[x] != x) x = uf[x] = uf[uf[x]];
        return x;
    };
    for(auto &[a, b] : gedges){
        int fa = find(a), fb = find(b);
        if(fa == fb){
            puts("NO");
            return 0;
        }
        uf[fa] = fb;
    }

    // Identify tree roots (nodes with no outgoing good edge)
    vector<int> roots;
    for(int i = 0; i < N; i++)
        if(par[i] == -1)
            roots.push_back(i);

    // Single tree already — just output good edges
    if((int)roots.size() == 1){
        for(auto &[a, b] : gedges)
            printf("%d %d\n", a, b);
        return 0;
    }

    // Read forbidden edges into hash set
    unordered_set<long long> forb;
    forb.reserve(K * 2);
    for(int i = 0; i < K; i++){
        int c, d;
        scanf("%d%d", &c, &d);
        forb.insert((long long)c * N + d);
    }
    auto bad = [&](int u, int v) -> bool {
        return forb.count((long long)u * N + v);
    };

    // Helper: DFS visiting all nodes of connected trees,
    // connecting unattached tree roots via non-forbidden edges.
    // When a root r is connected to node v, edge (r,v) is added
    // and r's subtree is explored next.
    auto sweep = [&](int start, set<int> &pool, vector<pair<int,int>> *out) {
        pool.erase(start);
        queue<int> tq;          // queue of tree-roots whose subtrees to explore
        tq.push(start);
        while(!tq.empty()){
            int tr = tq.front(); tq.pop();
            stack<int> stk;
            stk.push(tr);
            while(!stk.empty()){
                int v = stk.top(); stk.pop();
                // Try attaching every remaining root to v
                auto it = pool.begin();
                while(it != pool.end()){
                    int r = *it;
                    if(!bad(r, v)){
                        if(out) out->push_back({r, v});
                        it = pool.erase(it);
                        tq.push(r);
                    } else {
                        ++it;
                    }
                }
                for(int c : ch[v]) stk.push(c);
            }
        }
    };

    // Phase 1: Kosaraju-like — find candidate root.
    // Run successive sweeps; the last starting root is the candidate.
    int candidate = -1;
    {
        set<int> unvis(roots.begin(), roots.end());
        vector<bool> vis(N, false);

        for(int r : roots){
            if(!vis[r]){
                candidate = r;
                vis[r] = true;
                // Mark all roots reached from r as visited
                set<int> pool;
                for(auto it = unvis.begin(); it != unvis.end(); ){
                    if(*it == r){ it = unvis.erase(it); }
                    else { pool.insert(*it); ++it; }
                }
                // sweep connects roots from pool; we don't need edges yet
                sweep(r, pool, nullptr);
                // Remaining in pool are unvisited; put back
                // But those reached are gone from pool; mark visited
                for(int x : roots){
                    if(!vis[x] && pool.find(x) == pool.end()){
                        // x was in pool but got removed => reached
                        // Actually this logic is getting complicated.
                        // Let me redo phase 1 more simply.
                    }
                }
                // Hmm, let me redo this.
                break; // will redo below
            }
        }
    }// Cleaner Phase 1
    candidate = -1;
    {
        set<int> unvis(roots.begin(), roots.end());
        // We'll track which roots have been "visited" (reached by some sweep)
        // by removing them from unvis.
        // Successive sweeps: pick any unvisited root, sweep from it.
        // The last root picked is the candidate.
        while(!unvis.empty()){
            int start = *unvis.begin();
            candidate = start;
            // sweep removes reachable roots from unvis
            // but sweep's pool should be unvis minus start
            // Actually sweep erases start and connected ones from pool.
            // Let's just pass unvis directly:
            sweep(start, unvis, nullptr);
            // start and everything reachable from it via complement edges
            // have been removed from unvis.
        }
    }

    // Phase 2: Build tree from candidate
    {
        set<int> unconn(roots.begin(), roots.end());
        vector<pair<int,int>> nedges;

        sweep(candidate, unconn, &nedges);

        if(!unconn.empty()){
            puts("NO");
            return 0;
        }

        for(auto &[a, b] : gedges)
            printf("%d %d\n", a, b);
        for(auto &[a, b] : nedges)
            printf("%d %d\n", a, b);
    }

    return 0;
}

컴파일 시 표준 에러 (stderr) 메시지

Main.cpp: In function 'int main()':
Main.cpp:6:10: warning: ignoring return value of 'int scanf(const char*, ...)' declared with attribute 'warn_unused_result' [-Wunused-result]
    6 |     scanf("%d%d%d", &N, &M, &K);
      |     ~~~~~^~~~~~~~~~~~~~~~~~~~~~
Main.cpp:14:14: warning: ignoring return value of 'int scanf(const char*, ...)' declared with attribute 'warn_unused_result' [-Wunused-result]
   14 |         scanf("%d%d", &a, &b);
      |         ~~~~~^~~~~~~~~~~~~~~~
Main.cpp:59:14: warning: ignoring return value of 'int scanf(const char*, ...)' declared with attribute 'warn_unused_result' [-Wunused-result]
   59 |         scanf("%d%d", &c, &d);
      |         ~~~~~^~~~~~~~~~~~~~~~
#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...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...