ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CodeForces - 362D] Fools and Foolproof Roads
    문제 풀이/CodeForces 2023. 2. 24. 01:15

    난이도: 2100

     

    태그

    더보기
    • Greedy
    • Graph Theory
      • Connected Component
    • Disjoint Set Union
    • Priority Queue

     

    풀이

    1. 다른 Region에 있는 도시는 몇 번 연결해야 하는가?

    더보기

    다른 Region에 있는 도시를 하나 연결할 때마다 연결 요소의 개수가 1씩 줄어들게 됩니다.

    그러므로, 입력된 그래프의 연결 요소를 \( c \)라고 할 때, \( q-c \)번 연결해야겠죠.

     

    2. 답이 No인 경우는?

    더보기

    [1]에 따르면, 연결 횟수인 \( p \)는 \( 0 \le p \le q-c \)여야 합니다.

    이를 만족하지 못한다면, 답은 No가 되겠죠.

     

    No가 나오는 케이스가 하나 더 있는데, \( m = 0, p \neq 0, q = n \)입니다.

    미리 연결된 도시는 없고, 이 상태에서 도시는 연결해야 하는데 Region 개수를 줄이면 안 되는 경우죠.

    // 무슨 도시를 선택하든, 첫 연결은 Region의 개수를 줄이게 됩니다.

    // 그런데, Region의 개수가 줄어들면 안 되므로 불가능하죠.

     

    3. 다른 Region 간의 연결과 같은 Region 간의 연결 중 무엇을 먼저 해야 할까?

    더보기

    Exchange Argument를 생각해봅시다.

    다른 Region에 속한 두 도시 \( a, b \)와 같은 Region에 속한 두 도시 \( c, d \)를 두면, 아래와 같은 상황을 생각해볼 수 있습니다.

    // 편의상, \( a \)가 속한 Region을 \( A \)라고 표현하겠습니다.

    // 이는 \( b, c, d \)에 대해서도 마찬가지입니다.

    • \( A \neq C \), \( B \neq C \): 3개의 Region이 모두 다른 경우입니다.
      이 때에는 자명히 두 연결 간의 순서가 상관이 없겠죠.
    • \( A = C \): 두 간선이 공유하는 Region이 있는 경우입니다.
      // \( B = C \) 역시 이 케이스에 해당됩니다.
      이 때에는 \( A, B \)를 먼저 연결한 뒤 \( A, A \)를 연결해야 합니다.
      // 앞의 경우는, \( (|A|+|B|+1) + (1000) \)의 비용이 드는 반면,
      // 뒤의 경우는, \( (1000) + (|A|+1000+|B|+1) \)의 비용이 들기 때문입니다.

     

    그러니, 항상 다른 Region 간의 연결을 우선하면 됩니다.

    다르게 말하자면, 다른 Region 간의 연결부터 다 한 뒤 남은 건 같은 Region 간의 연결로 때우면 된다는 의미죠.

     

    4. 다른 Region에 있는 도시 간의 연결 순서는 어떻게 되어야 할까?

    더보기

    유명한 그리디 문제입니다.

    현재까지 사용된 간선 합이 가장 적은 두 Region을 선택한 뒤 합쳐주면 되죠.

     

    /// [TODO] 여기에 증명 적기

     

    5. 코드

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    int par[100020]; ll siz[100020];
    int fnd(int v){ return par[v] = (par[v]==v ? v : fnd(par[v])); }
    void uni(int v, int w, ll x){
        int pv = fnd(v), pw = fnd(w);
        if (pv == pw){ siz[pv] += x; }
        else{ par[pv] = pw; siz[pw] += siz[pv] + x; }
        siz[pw] = min<ll>(siz[pw], 1e9);
    }
     
    void Main(){
        int n, m, k, t; cin >> n >> m >> k >> t;
        for (int i = 1; i <= n; i++){ par[i] = i; }
        if (m == 0){
            if (k > 0 && n == t){ cout << "NO"return; }
        }
        while (m--){
            int v, w; ll x; cin >> v >> w >> x; uni(v, w, x);
        }
        priority_stack<pl2> pq;
        for (int i = 1; i <= n; i++){
            if (par[i] != i){ continue; }
            pq.push({siz[i], i});
        }
        int cnt = pq.size();
        if (t > cnt || cnt-> t){ cout << "NO"return; }
        cout << "YES" << endl;
        while (cnt > t){
            pl2 p1 = pq.top(); pq.pop();
            pl2 p2 = pq.top(); pq.pop();
            ll a = p1.fr, b = p2.fr;
            int v = p1.sc, w = p2.sc;
            cout << v << ' ' << w << endl;
            uni(v, w, a+b+1); pq.push({siz[w], w});
            cnt -= 1; k -= 1;
        }
        int v = 0, w = 0;
        for (int i = 1; i <= n; i++){
            if (par[i] != i){ v = i; w = fnd(i); }
        }
        while (k--){ cout << v << ' ' << w << endl; }
    }
    cs
Designed by Tistory.