You are on page 1of 22

안전한 비상 연락망

한국정보올림피아드 (KOI) 2014년 본선 고등4


구재현 (gs14004)
Problem

• 정점 N, 간선 M개의 가중치 있는 양방향 그래프가 주어


진다.

• 각 간선에 대해서, 그 간선이 제거되었을 때의 최소 스패


닝 트리의 크기를 구하면?

• N <= 100,000 / M <= 300,000


최소 스패닝 트리란?

• http://navercast.naver.com/contents.nhn?
rid=2871&contents_id=86840

• 그래프의 모든 정점을 최소 비용으로 연결하는 트리!


1. O(M^2lgM)
• 최소 스패닝 트리는 일반적으로 크루스칼 알고리즘을 사
용해서 해결한다.

• (크루스칼 알고리즘과 Union-Find를 모른다면, “프로그


래밍 콘테스트 챌린지”를 꼭 찾아보길!)

• 크루스칼 알고리즘은 간선의 정렬만으로 쉽게 풀 수 있어


O(MlgM)의 시간 복잡도를 가진다.

• 간선을 제거하고 크루스칼 알고리즘을 그때그때 돌리면


O(M^2lgM)의 시간 복잡도로 해결할 수 있다.
2. O(NMlgM)
• 이를 약간 더 최적화할 수 있는 방법이 있다.

• 아무 간선도 제거하지 않은 상태에서의 스패닝 트리를 구


한다.

• 이 스패닝 트리 안에 포함되지 않은 간선을 제거해도, 스


패닝 트리는 일정하므로 이때를 계산해 줄 필요가 없다.

• 스패닝 트리안에는 N-1개의 간선이 있으므로 O(NMlgM)


3. O(NM)
• 크루스칼 알고리즘은 그때 그때 간선을 정렬하는데, 간선
의 변화가 없는 이상 두번 정렬할 필요가 없다.

• 정렬과 MST 계산을 처음에 해놓고, Union-Find 트리만


초기화!

• Union-Find 트리는 준 상수시간에 동작하므로 O(NM)

• 이보다 더 빠르게 하는 방법은?


4. O(MlgM) 1
• 이미 구해놓은 Minimum Spanning Tree를 생각해 보자.

• 트리에서 간선 하나를 제거했다는 것은 트리가 두개로 쪼


개졌다는 것을 의미한다.

• 이 때 추가하게 될 간선은 MST에 포함 되지 않으며, 두


쪼개진 트리를 잇는 간선 중 최소 비용을 가진 간선이다.

• Union Find로 적당히 그룹을 정해놓은 후 M개의 간선 후


보를 돌면, 여전히 O(NM)에 풀 수 있다.
4. O(MlgM) 2
• 이제 관점을 바꿔서 트리 밖에 있는 간선을 생각해보자.

• 트리 밖에 있는 간선 X가 (t - u)를 잇는다면, (t - 트리상


경로 - u - X - t) 형태의 사이클이 생긴다.

• 사이클 안에 있는 간선 중 하나가 빠지면 다시 트리가 된


다.

• -> (t - u) 상 경로 안에 있는 모든 간선은, (t - u)를 잇는


트리 밖의 간선 X를 대체제로 사용할 수 있다.
4. O(MlgM) 3
• 만약에 경로상에 존재하는 간선이 이미 X보다 나은 (비용
이 적은) 대체제를 사용했다면, 건드리지 말고

• 그 외의 경우에는 대체제를 X로 업데이트 해준다.

• 앞서 말한 연산을, 비용이 적은 간선부터 넣지 않고 큰 간


선부터 넣는다면, 무작정 칠해주면 되기 때문에 더 쉽게
구현할 수 있다.

• 이로써 이 문제를 푸는데 필요한 것은 트리에 빠르게 값을


칠해주는 자료 구조라는 것을 알 수 있다!
4. O(MlgM) 4

• 벽 칠하기 (koistudy #877) 의 트리 버전???


4. O(MlgM) 5
• 앞서 제시한 문제는

• 1. Union - Find 류의 테크닉으로 O(MlgM)

• 2. Plane Sweeping으로 O(M)

• 3. Segment Tree로 O(MlgM)

• .. 에 해결하는 등 다양한 풀이가 있다.


4. O(MlgM) 6
• .. 트리에서는

• 1. Union - Find 류의 테크닉으로 O(MlgM)

• 2. Plane Sweeping으로 O(MlgM)

• 3. Segment Tree로 O(Mlg^2M)

• 세 풀이 모두 트리에서 똑같이 적용 가능하다.

• 1번 풀이를 설명.
4. O(MlgM) 7
• 벽 칠하기 http://codepad.org/sFgD1Clj

• nxt[i] 라는 배열을 관리한다. 기본적으로 다음 포인터라


는 말이며 nxt[i] = i+1;

• [s,e]에 구간을 칠할 때 마다 nxt[i]를 e로 업데이트 시켜


준다. 그 사이에 있는 값을 볼 필요가 없으니 다음 포인터
를 이렇게 커팅 (?) 해주는 것이다.

• 시간 복잡도는 O(MlgM). 왜? http://codeforces.com/


blog/entry/21476
4. O(MlgM) 8
• 트리에 이를 동일하게 적용시키려
면 먼저 LCA라는 개념과 친숙해져
야 한다.

• 루트가 있는 트리에서, (루트 - A)


와 (루트 - B) 라는 경로의 교집합
은 (루트 - C) 일 것이다. C를 구하
는 법은?

• Naive는 O(N), O(lgN)을 곧 설명


4. O(MlgM) 9
• Q : 이 트리, 루트가 어디에요?

• A : 그냥 1번이라고 잡읍시다. 상관 없어요.

• 앞서 벽 칠하기와 비슷하게, i -> i의 조상으로 가는 nxt[i]를 구


성할 수 있음.

• A - B 간의 쿼리를 칠하려면, A -> LCA(A,B) / B -> LCA(A,B)


로 쿼리를 칠하면 됨.

• nxt[i]는 아까와 비슷하게 업데이트 해주면 칠하는 과정은 합


산시 O(M)
4. O(MlgM) 10
• 이를 토대로 이 문제의 쿼리 복잡도는 LCA를 구하는데
걸리는 시간인 O(MlgN) 임을 알수 있다 >_<

• MST가 O(MlgM)이니까 충분히 빠르다.

• LCA를 구하는 방법 역시 nxt[i]를 적당히 가지고 노는 방


법이다.

• nxt[i] 만 가지고 LCA를 구하는 방법은.. 뭐가 있을까?


4. O(MlgM) 11

• O(N)에 nxt 배열만 가지고 LCA를 구하려면..

• 먼저 1번 정점에서의 깊이를 맞춰준다.

• 편의상 dep(A) <= dep(B) 라 하면 B를 dep(B) - dep(A)


번 nxt를 타게 시키면 된다.

• 이후에는 그냥 while(A != B) B = nxt[B], A = nxt[A]


4. O(MlgM) 12
• O(N^2) 크기의 nxt[x][i] 를 정의하면 빠르게 풀 수 있다!

• B = nxt[B][dep(B) - dep(A)] 로 레벨을 맞춰주고.

• Parametric Search를 사용해서, nxt[A][i] == nxt[B][i]


인 최소의 i를 검색.

• O(NlgN)

• 하지만 O(N^2) 라는 말도 안되는 nxt의 크기..


4. O(MlgM) 13
• 버킷 법으로도 충분한 최적화가 가능하다 (AC는 장담 못
하지만). 하지만 설명은 생략

• nxt[x][i] = x의 2^i번째 조상!

• dep(B) - dep(A)를 이진수로 나열했을 때 비트 수는 lgN


개니 O(lgN)에 매치 가능.

• nxt[A][i] = nxt[B][i]인 최소의 i?


4. O(MlgM) 14
• nxt[x][i] = x의 2^i번째 조상!

• nxt[A][i] = nxt[B][i]인 최소의 i?

• nxt[A][i] != nxt[B][i]인 최소의 i의 조상!

• for(int i=LOGN; i>=0; i—){

• if(nxt[A][i] != nxt[B][i]) A = nxt[A][i], B = nxt[B][i] }

• if(A != B) return nxt[A][0], return A otherwise


4. O(MlgM) 15

• 큰 놈부터 차근차근 적용시키면서 LCA를 찾을 수 있다!

• 시간 복잡도는 O(MlgM)

• http://codepad.org/nBPTrMQ3
다른 풀이
• 세그먼트 트리를 사용해서 이 문제를 풀려면 Heavy-Light Decomposition
이라는 방법을 쓰면 된다.

• 세그먼트 트리는 일직선에서만 작동하기에 트리를 일직선의 집합으로 분해


해 버리는 방법이다.

• 이렇게 트리를 분해할 때, 어떠한 경로도 O(lgN)개 이상의 일직선을 보지 않


도록 분해할 수 있다.

• 일직선을 처리하는 것은 O(lgN)에 가능하니 이 방법을 사용하면 O(Mlg^2N)


에 문제를 풀 수 있다.

• 트리에서 쿼리 처리하는 가장 강력한 방법 중 하나이다. 재밌으니 공부해두는


것도 괜찮다. http://blog.anudeep2011.com/heavy-light-decomposition/

You might also like