Table of Contents
개요
DP 점화식이 이전의 항 몇 개의 선형결합으로 나타내지는 경우, 즉 선형방정식의 꼴일 경우, n번째 항 을 행렬 거듭제곱을 이용하여 시간에 빠르게 구할 수 있다.
선행 지식
분할 정복을 이용한 빠른 행렬 거듭제곱
내가 생각하는 순서
점화식을 도출하거나, 잘 짜맞췄더니 점화식이 개요에서 설명했던 저런 꼴이 될 경우, 나는 보통 이 순서대로 생각한다.
우리가 처음으로 만들 식은 대충
이러한 꼴이 될 것이다. 편의상 우변의 행렬곱에서 왼쪽 행렬을 , 오른쪽 행렬을 라 하자.
이 때, 우변은 행렬과 행렬의 곱이 되며, 당연히 그 결과는 행렬이다. (행렬 크기 은 풀이에 따라 달라질 수 있다)
좌변의 첫 행 의 값이 꼴의 점화식이 나오도록 의 첫 행을 적절히 잡는다. 의 첫 행과 를 잘 생각하여, 의 어떤 항을 살리고 어떤 항을 죽여야 하는지, 계수가 붙어있다면 고려하며 숫자를 채워넣는다.
좌변의 나머지 행에 대해서도 의 나머지 행을 잘 맞춰준다. 아마 첫 행인 만 완성했다면 그 이후는 자기 자신이 등장하도록 적절히 하나만 트리거해주면 될 것이다. 이렇게 하여 행렬을 완성한다.
우변에서 행렬을 거듭제곱할 때마다 의 원소들의 차수가 하나씩 줄어든다. 행렬을 몇 번 거듭제곱하여야 가 에 종속적이지 않게 되는지, 고정된 숫자로 나올 수 있는지 계산한다.
식이 완성되었다면, 빠른 행렬 거듭제곱과 행렬곱만 잘 해주면 끝난다.
예제로 다시 한 번 살펴보자.
Remind: 피보나치 수
번째 피보나치 수 을 행렬을 이용해 로그 시간에 구하는 방법은 익히 알려져 있다.
인데, 우선 에 주목해 보자.
을 과 두 개의 항으로 나타내야 하므로, 좌변과 행렬을 적절히 잡아보자.
좌변에서 을 점화식대로 맞춰주려면, 의 첫 행을 어떻게 채우면 될까?
1, 1로 채우면, 좌변의 첫 행의 원소에 점화식대로 결과값이 들어가게 된다.
좌변의 이 남는데, 얘는 에서 그대로 가져오면 된다. 그렇게 하려면 행렬의 나머지를 어떻게 채우면 될까?
1, 0으로 채우면 이다. 말 그대로이다. 일단 기본적인 식이 완성되었다. 여기서 조금만 더 변형을 가해보자. 우변의 의 차수를 하나씩 줄여나간다고 생각하면 편하다.
우변의 맨 앞에 를 하나 곱하면, 의 모든 항의 차수가 하나씩 줄어들게 된다.
인 경우를 생각해보자. 써 보면 다음과 같다.
여기서 우변의 맨 앞에 를 하나 더 곱하면?
뭐 이렇게 되겠다. 의 원소들의 차수가 하나씩 줄어든 것을 확인할 수 있다. 한번 더 하면?
그런데, 과 는 각각 1과 0으로 상수값이다. 따라서 마지막 식을 다시 써 보면,
모든 과정이 끝났다. 우변의 (세제곱된) 는 빠른 행렬 거듭제곱을 이용하여 로그 시간에 구할 수 있고, 우변의 계산 결과인 행렬의 첫 행의 원소가 우리가 구하는 가 된다.
방금은 인 경우를 예로 들었는데, 이를 일반화하면 다음과 같은 식을 도출할 수 있다.
재귀적이지 않은, 바로 계산 가능한 일반화된 식이 만들어진 것이다. 이 문단에서 설명한 방법으로 BOJ#2749 - 피보나치 수 3 (https://www.acmicpc.net/problem/2749)을 풀 수 있다.
다른 문제를 몇 개 풀어보자.
BOJ#14440 - 정수 수열
점화식은 문제에 주어져있듯 이다.
이 과 두 개의 항으로 나타내어져 있으므로, 대충 틀을 잡아보자.
설명의 편의상 쓰는 와 문제에서 주어진 은 다르다. 어떻게 쓰다 보니까 겹쳤네... 암튼 이 점화식대로 계산되려면?
은 에서 그대로 가져다 쓰면 되니까, 1과 0으로 채워두면 만 살고 는 죽겠지?
이제 차수를 줄여보자. 의 차수를 적절히 줄여 고정된 식을 만드려면 를 몇 번 거듭제곱하면 될까?
과 은 입력에서 주어진다. 끝!
BOJ#17272 - 리그 오브 레전설 (Large)
을 "초를 채울 수 있는 경우의 수"라 정의하면, 조금만 생각해서 다음과 같은 점화식을 생각해볼 수 있다.
초까지 채운 경우의 수는, 마지막에서 1초 떼서 A 스킬 쓰는 경우의 수와 마지막에서 초 떼서 B 스킬 쓰는 경우의 수의 합이다. 끝자리 경우로 분할하는 DP와 비슷하다.
은 1이라 하자.
을 나타내는 우변의 항이 까지 등장하므로, 에 이를 나타내려면 우변의 에 부터 까지의 항이 모두 등장해야 한다. 조금만 생각해보면, 다음과 같은 틀이 나온다.
좌변과 의 항들이 어디까지 가는지 범위에 주의하자. 행렬이 부터 시작해서 까지 나와야 하므로, 개수를 잘 세서 좌변에도 맞춰주면 된다.
좌변의 에 점화식대로 꽂아주려면, 의 첫 행은?
좌변의 부터는 의 값을 그대로 갖다 쓰면 되니까, 적절히 살릴 걸 살리고 죽일 걸 죽이며 행렬을 완성하자.
의 예시를 들면, 다음과 같은 식이 완성된 셈이다.
이제 의 거듭제곱을 통해 의 차수를 줄여보자. 미만의 항들은 편의상 0이라 가정하자. 최대한 줄이면 다음과 같다.
그래서, 이를 일반화해보면, 다음과 같은 식이 나온다.
끝!
BOJ#16467 - 병아리의 변신은 무죄
이 문제는 바로 위에서 설명한 문제와 거의 동일하게 풀 수 있다. 앞부분만 설명하면 눈치챌지도 모르겠다.
을 일 후의 병아리의 수라 하면, 다음 점화식이 성립한다.
또 그 꼴이다! 선형방정식 꼴이고, 좌변 은 과 의 합으로 나타난다. 행렬은 이 모두 나타나겠고, 이 행렬의 길이만큼 좌변의 행렬이 정의될 것이다. 한번 써 보면 다음과 같다.
위에서 설명한 문제와 아예 동일하다. 이제 행렬 채우고, 행렬의 차수를 줄여나가면 된다.
BOJ#13328 - Message Passing
비슷한 방법으로 풀리는 것 같은데 아직 안 풀어봤다 나중에 풀어보고 풀이 추가하기