Skip to main content
  1. Posts/
  2. Algorithm/

BOJ 31055 A Graph Problem

·57 words·1 min
Jiho Kim
Author
Jiho Kim
달려 또 달려

📝 문제 정보
#

문제 번역
#

Bessie는 수학 지식을 향상시키기 위해 그래프 이론 강좌를 수강하고 있는데, 다음 문제에서 막혔습니다. 그녀를 도와주세요!

연결된 무방향 그래프가 주어집니다. 정점은 $1\dots N$으로, 간선은 $1\dots M$으로 라벨링되어 있습니다 ($2\le N\le 2\cdot 10^5$, $N-1\le M\le 4\cdot 10^5$).

그래프의 각 정점 $v$에 대해 다음 과정을 수행합니다:

  1. $S=\{v\}$, $h=0$으로 설정합니다.
  2. $|S|
  3. 정확히 한쪽 끝점만 $S$에 속한 모든 간선 중에서, 라벨이 가장 작은 간선을 $e$라고 합니다.
  4. $S$에 속하지 않은 $e$의 끝점을 $S$에 추가합니다.
  5. $h=10h+e$로 설정합니다.
  • $h\pmod{10^9+7}$을 반환합니다.
  • 이 과정의 모든 반환값을 구하세요.

    입력
    #

    첫 번째 줄에 $N$과 $M$이 주어집니다.

    다음 $M$개의 줄에는 $e$번째 간선의 양 끝점 $(a_e,b_e)$가 주어집니다 ($1\le a_e

    이 간선들이 연결된 그래프를 형성하며, 각 정점 쌍을 연결하는 간선은 최대 1개임이 보장됩니다.

    출력
    #

    $N$개의 줄을 출력합니다. $i$번째 줄에는 정점 $i$에서 시작하는 과정의 반환값을 출력합니다.

    🧐 관찰 및 접근
    #

    • 무향그래프에서, 각 정점에 대해 외부 간선이랑 연결되는 = 분리집합의 크기가 커지는.. 느낌으로 하는 거 같다.
      • 대애충 누가봐도 크루스칼맛인데
    • 일단 예제 입력 1을 봐보자.
      • 1의 입장에서, $S = \{1\}, h = 0$
      • 2가 연결되면서, $S = \{1, 2\}, h = 1$
      • 3이 연결되면서, $S = \{1, 2, 3\}, h = 12$
    • 흠.. 이론상 그냥 유파를 $N$개 들고있으면 되긴 하는데 ㅋㅋ 메모리가 터지겠지?
      • 아, 유파 하나만 들고있어도 되넹
    • 간선 순서대로 했을때, 한번 연결되면 그뒤로는 그냥 평생 같은취급을 해버려도 되는디..
    • 일단 유파를 돌리면서, 연결되면 그 두 트리에 있는 친구들은 다 $h$값을 갱신하면 된단말이야
      • 이걸 뭔가 레이지하게 못하나?
      • 트리를 잘 피면 될거같은데.. 일단 간선은 $N-1$개만 남길 수 있다.
      • 연결리스트 느낌으로 관리하자.

    💻 풀이
    #

    • 코드 (C++):
    void solve(){
        cin >> N >> M;
        UnionFind UF(N+1), UF_head(N+1), UF_tail(N+1);
        rep(i, 0, M){
            int a, b; cin >> a >> b;
            if(UF.merge(a, b)){
                edges.push_back({a, b, i+1});
                int a_tail = UF_tail.find(a);
                int b_head = UF_head.find(b);
                nxt[a_tail] = b_head;
                UF_head.merge(a, b);
                UF_tail.merge(b, a);
            }
        }
    
        vector<int> order;
        rep(i, 1, N+1) if(UF_head.find(i) == i){
            int cur = i;
            while(cur != 0){
                order.push_back(cur);
                cur = nxt[cur];
            }
        }
        vector<int> to_idx(N+1, -1);
        rep(i, 0, N) to_idx[order[i]] = i;
    
        // for(auto x: order) cout << x << " "; cout << "\n";
    
        UF_head.init(N+1);
        UF_tail.init(N+1);
        LazySegmentTree LST(N);
        mint m10 = 10;
        for(auto [a, b, w]: edges){
            int LH = UF_head.find(a), LT = UF_tail.find(a);
            int RH = UF_head.find(b), RT = UF_tail.find(b);
            LH = to_idx[LH];
            LT = to_idx[LT];
            RH = to_idx[RH];
            RT = to_idx[RT];
            // cout << LH << " " << LT << " " << RH << " " << RT << "\n";
    
            mint Lval = LST.query(to_idx[a], to_idx[a]), Rval = LST.query(to_idx[b], to_idx[b]);
            LST.update(LH, RT, {10, w});
            LST.update(LH, LT, {m10.pow(UF_head.cnt[UF_head.find(b)]-1), Rval});
            LST.update(RH, RT, {m10.pow(UF_tail.cnt[UF_tail.find(a)]-1), Lval});
            UF_head.merge(a, b);
            UF_tail.merge(b, a);
        }
    
        rep(i, 1, N+1) cout << LST.query(to_idx[i], to_idx[i]) << "\n";
    }
    🔒

    구현 코드 잠금

    아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.
    광고 수익이 블로그 운영에 도움이 됩니다 🙏

    🛒 쿠팡 방문하고 코드 보기

    방문 후 잠금이 자동으로 해제됩니다