본문 바로가기

BOJ

[BOJ 22954] 그래프 트리 분할

https://www.acmicpc.net/problem/22954

 

22954번: 그래프 트리 분할

첫 번째 줄에 정점의 개수 $N$, 간선의 개수$M$이 주어진다. ($1 \le N \le 100\,000$, $0 \le M \le 200\,000$) 두 번째 줄부터 $M$줄에 걸쳐서 간선을 나타내는 정수 $u$와 $v$가 주어진다. ($1 \le u, v \le N$, $u

www.acmicpc.net

Tag : case work, dsu, constructive

 

문제요약

무향 그래프 \(G(N, M)\)이 주어진다. 간선을 원하는 만큼 삭제해 서로 다른 크기의 트리 2개로 분할하라.

불가능한 경우는 -1을 출력한다.

 

풀이

새벽에 심심해서 tony9402님이 골라온 문제를 풀었다.

일단 일반 그래프인 것이 거슬린다. 트리라면 분할하기 꽤 쉬울텐데...

이는 스패닝 트리를 아무것이나 구해주면 된다.

구한 스패닝 트리에서 조건에 맞는 분할이 불가능하다면 애초에 불가능한 입력이다.

(스패닝 트리는 dsu로 통해 구할 수 있다)

 

그럼 이제 불가능한 경우를 따질 수 있다.

1. 간선을 모두 돌았는데 스패닝 트리를 만들 수 없을 때

    1) 이 때 컴포넌트가 2개일 때

        1) 두 컴포넌트의 크기가 다를 때는 그대로 출력하면 된다. ( O )

        2) 두 컴포넌트의 크기가 같을 때는 불가능하다. ( X )

    2) 컴포넌트가 3개 이상이면 불가능하다. 간선을 추가할 수는 없기 때문 ( X )

2. 스패닝 트리에서 sz[root] != n - sz[i]인 i가 존재하지 않을 때 ( X )

    sz[i]는 i를 루트로 가지는 서브트리의 크기를 의미한다. 

    parent[i] -> i 간선을 끊었을 때 sz[i]는 끊은 간선의 아래쪽 서브트리 크기

    sz[root] - sz[i]는 root와 연결된 쪽의 트리 크기를 의미한다. 

3. n <= 2인 경우 ( X )

    이 경우는 사실 1, 2에서 잘 걸러지지만 문제를 보고 가장 먼저 생각난 케이스라

    if(n<=2) { cout << -1 << endl; return 0; }을 코드에 타이핑해놓고 풀이를 구상했다.

 

이를 잘 구현해주면 맞는다. (나는 1.2.2를 구현에서 놓치고 맞왜틀했었다..)

풀이를 구상하고 코딩하기 전까지 이게 골드 상위까지 되나? 싶었는데 구현량이 생각보다 많다.

구현난이도가 좀 반영된 듯 하다. 

 

 

전체코드

#include "bits/stdc++.h"
#define endl '\n'

using namespace std;
using ll = long long;

struct DSU{
    vector<int>pa;int n;
    DSU(int _n):n(_n){pa.resize(n+1);iota(pa.begin(),pa.end(),0);}
    int find(int a){return pa[a]==a?a:(pa[a]=find(pa[a]));}
    void merge(int a,int b){pa[find(b)]=find(a);}
};

int N, M;
vector<pair<int, int>> edge;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    
    cin >> N >> M;
    if(N <= 2) {
        cout << -1 << endl;
        return 0;
    }
    edge.resize(M + 1);
    shared_ptr<DSU> uf = make_shared<DSU>(N);
    vector<int> v;
    for(int i=1; i<=M; i++) {
        cin >> edge[i].first >> edge[i].second;
        if(uf->find(edge[i].first) != uf->find(edge[i].second)) {
            uf->merge(edge[i].first, edge[i].second);
            v.push_back(i);
        }
    }
    auto two = [&]()->void{
        vector<int> a, b;
        a.push_back(1);
        for(int i=2; i<=N; i++)
            if(uf->find(1) == uf->find(i)) a.push_back(i);
            else b.push_back(i);
        if(a.size() == b.size()) {
            cout << -1 << endl;
            exit(0);
        }
        cout << a.size() << ' ' << b.size() << endl;
        for(auto &i : a) cout << i << ' '; cout << endl;
        for(auto &i : v) if(uf->find(1) == uf->find(edge[i].first))
            cout << i << ' '; cout << endl;
        for(auto &i : b) cout << i << ' '; cout << endl;
        for(auto &i : v) if(uf->find(1) != uf->find(edge[i].first))
            cout << i << ' '; cout << endl;
    };
    {
        int cnt = N - (int)v.size();
        if(cnt > 2) {
            cout << -1 << endl;
            return 0;
        }
        if(cnt == 2) {
            two();
            return 0;
        }
    }
    vector<vector<int>>g(N + 1);
    for(auto &k : v) {
        auto [i, j] = edge[k];
        g[i].push_back(j);
        g[j].push_back(i);
    }
    vector<int> chk(N + 1), sz(N + 1), pa(N + 1);
    function<int(int)> dfs = [&](int x)->int{
        chk[x] = 1;
        sz[x] = 1;
        for(auto &i : g[x]) if(!chk[i]) {
            pa[i] = x;
            sz[x] += dfs(i);
        }
        return sz[x];
    };
    dfs(1);
    pair<int, int> e(0, 0);
    for(int i=2; i<=N; i++) {
        if((N%2 or sz[i] != N/2) and sz[i] < N) {
            e = make_pair(i, pa[i]);
            break;
        }
    }
    uf = make_shared<DSU>(N);
    vector<int> t;
    for(auto &i : v){
        auto [p, q] = edge[i];
        if(e == make_pair(p, q) or e == make_pair(q, p)) continue;
        t.push_back(i);
        uf->merge(p, q);
    }
    v.clear(); for(auto &i : t) v.push_back(i);
    two();
}

'BOJ' 카테고리의 다른 글

[BOJ 2379] 트리 탐색하기  (0) 2022.01.08
[BOJ 20930] 우주 정거장  (0) 2022.01.08
[BOJ 20532] 정점 간 통신 네트워크  (0) 2022.01.08
[BOJ 20929] 중간  (0) 2022.01.08
[BOJ 23719] K번째 음식 찾기 1  (0) 2022.01.08