Algorithm/BOJ

[백준 17398] 통신망 분할 (C++)

Henu 2021. 10. 13. 23:50
 

17398번: 통신망 분할

첫 번째 줄에는 통신탑의 개수인 자연수 N, 통신탑 사이의 연결의 개수인 자연수 M, 통신망 연결 분할 횟수인 자연수 Q가 공백으로 구분되어 주어진다. (1 ≤ N ≤ 100,000, 1 ≤ M ≤ 100,000, 1 ≤ Q 

www.acmicpc.net

 

문제를 읽어보니 통신망들의 집합을 구현해야 했다.

서로 연결이 되어있는 통신망, 즉 하나의 정점으로 귀납되는 노드들 사이의 한 간선을 끊으면 2개로 나뉠 텐데 몇 개씩으로 나눠지는지를 구하면 된다.

 

풀이 방법을 떠올리기까지 꽤 오래 걸렸는데 처음에 노드의 간선을 끊는 동작을 하나의 쿼리로 보고 세그먼트 트리를 구성해서 풀어야 하나?라는 말만 그럴듯한 이상한 생각을 했다.

 

그러다가 일단 input에 입력을 다 받고 보니 반대로 생각해서 통신망이 주어진대로 다 끊어져 있는 상태에서 끊는 경우의 역순으로 다시 연결한다면 답을 똑같이 구할 수 있지 않을까 생각이 들어서 풀게 되었다. 물론 유니온 파인드를 사용했고 두 집합을 연결하기 전에 각각 몇 개의 노드가 연결되어있는지는 node[]에 -1을 곱하면 알 수 있으므로 쉽게 구할 수 있다. (node배열을 -1로 초기화했기 때문임)

 

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <vector>
#include <deque>
#include <cstring>
#define fastio ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define pii pair<intint>
typedef long long ll;
 
using namespace std;
 
int N, M, Q;
ll ans;
 
deque<int> Q_list;
vector<pii > link_list;
bool isuse[100001];
int node[100001];
 
void input(){
    cin >> N >> M >> Q;
    int u, v;
    for(int i = 0; i < M; i++){
        cin >> u >> v;
        link_list.push_back({u, v});
    }
    for(int i = 0; i < Q; i++){
        cin >> u;
        Q_list.push_front(u);
        isuse[u] = true;
    }
    memset(node, -1sizeof(node));
}
 
int findtopnode(int a){
    if(node[a] < 0return a;
    return node[a] = findtopnode(node[a]);
}
 
ll Union_node(int a, int b){
    a = findtopnode(a);
    b = findtopnode(b);
    
    if(a == b) return 0;
    
    ll ret = node[a] * node[b];
    
    if(node[a] < node[b]){
        node[a] += node[b];
        node[b] = a;
    }
    else{
        node[b] += node[a];
        node[a] = b;
    }
    
    return ret;
}
 
void solve(){
    // 통신망이 연결됨을 확인하는 방법 : 분리 집합?
    
    // 연결을 끊었을 때 두 개로 나눠짐을 어떻게 파악하지?
    // 끊어질 때마다 처음부터 유니온 파인드 실행?? 절대 안됨
    // 세그먼트 트리??
    // 연결을 끊는다는걸 하나의 쿼리로 생각?
    
    // 1. 분리 집합의 최적화..
    // 쿼리는 최대 10만개
    // 연결도 최대 10만개
    // 아 미리 제거해놓고 제거 역순으로 다시 연결시키면서
    // 연결될 때 서로 몇개의 원소를 가지고 연결되는지 확인
    
    for(int i = 0; i < link_list.size(); i++){
        if(isuse[i+1]) continue;
        int u = link_list[i].first;
        int v = link_list[i].second;
        Union_node(u, v);
    }
    
    for(auto &q: Q_list){
        int u = link_list[q-1].first;
        int v = link_list[q-1].second;
        ans += Union_node(u, v);
    }
    
    
    cout << ans;
}
 
int main(){
    fastio
    input();
    solve();
    
    return 0;
}
 
cs