홈 BOJ 20040 — 사이클 게임
글
취소

BOJ 20040 — 사이클 게임

문제 링크

간선을 입력 순서대로 처리합니다. 간선 (a, b)를 추가하기 전에, 앞서 처리한 간선으로 이루어진 그래프에서 두 정점의 연결 요소 대표를 찾습니다. 대표가 같다면 이미 a와 b를 잇는 경로가 있으므로 이 간선을 추가할 때 사이클이 생깁니다. 따라서 그 차례를 1부터 세어 즉시 출력합니다. 대표가 다르면 두 연결 요소를 합칩니다. 경로로 이미 연결된 두 정점을 잇는 간선이 추가될 때만 사이클이 생기므로, 이 방법은 처음 사이클을 만드는 간선을 찾습니다. 자기 자신으로 향하는 간선도 입력된 차례에 검출되며, 같은 두 정점을 잇는 평행 간선은 두 번째 간선이 사이클을 만듭니다.

서로소 집합 자료구조는 루트의 연결 요소 크기를 음수로 저장합니다. find는 경로 압축을 적용하고, union은 크기가 작은 연결 요소를 큰 연결 요소 아래에 붙입니다. 두 최적화를 함께 사용하면 연산당 분할 상환 시간은 O(α(N))이며, 간선 M개를 처리하는 시간은 O(M α(N)), 자료구조의 공간은 O(N)입니다. 사이클이 생기는 간선이 없으면 0을 출력합니다.

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
import java.io.*;

public class Main {
  static class FastScanner {
    private final InputStream input;
    private final byte[] buffer = new byte[1 << 16];
    private int length = 0;
    private int pointer = 0;

    FastScanner(InputStream input) {
      this.input = input;
    }

    private int read() throws IOException {
      if (pointer == length) {
        length = input.read(buffer);
        pointer = 0;
        if (length == -1) return -1;
      }
      return buffer[pointer++];
    }

    int nextInt() throws IOException {
      int c;
      do {
        c = read();
      } while (c <= ' ' && c != -1);

      int value = 0;
      while (c > ' ') {
        value = value * 10 + c - '0';
        c = read();
      }
      return value;
    }
  }

  static class DisjointSet {
    private final int[] parentOrSize;

    DisjointSet(int n) {
      parentOrSize = new int[n];
      for (int i = 0; i < n; i++) parentOrSize[i] = -1;
    }

    int find(int node) {
      if (parentOrSize[node] < 0) return node;
      return parentOrSize[node] = find(parentOrSize[node]);
    }

    boolean union(int a, int b) {
      int rootA = find(a);
      int rootB = find(b);
      if (rootA == rootB) return false;

      if (parentOrSize[rootA] > parentOrSize[rootB]) {
        int temp = rootA;
        rootA = rootB;
        rootB = temp;
      }
      parentOrSize[rootA] += parentOrSize[rootB];
      parentOrSize[rootB] = rootA;
      return true;
    }
  }

  public static void main(String[] args) throws IOException {
    FastScanner input = new FastScanner(System.in);
    int n = input.nextInt();
    int m = input.nextInt();
    DisjointSet dsu = new DisjointSet(n);

    for (int turn = 1; turn <= m; turn++) {
      int a = input.nextInt() - 1;
      int b = input.nextInt() - 1;
      if (!dsu.union(a, b)) {
        System.out.println(turn);
        return;
      }
    }

    System.out.println(0);
  }
}
이 글은 저자가 CC BY 4.0 라이선스로 배포합니다.