https://www.acmicpc.net/problem/25962
25962번: 선우의 셋리스트
첫 번째 줄에 선우가 공연을 위해 준비해야 하는 가능한 셋리스트의 경우의 수를 $1\,000\,000\,007$($=10^{9}+7$)로 나눈 나머지를 출력하시오. 셋리스트를 구성할 수 없다면 정답은 $0$이다.
www.acmicpc.net
잘 관찰하면 이 문제는 $1 \times N$ 크기의 직사각형을 $1 \times 1$부터 $1 \times 5$ 크기의 블록으로 타일링하는 경우의 수를 구하는 문제와 같다고 할 수 있습니다.
몇 가지 정의하겠습니다.
1분짜리, 2분짜리, 3분짜리, 4분짜리, 5분짜리 노래의 개수를 각각 $M_{1}$, $M_{2}$, $M_{3}$, $M_{4}$, $M_{5}$ 라고 하고,
$N$에 대한 셋리스트의 개수를 $A \left ( N \right )$라고 하겠습니다.
이제 작은 $N$에 대해 경우의 수를 구해보겠습니다.
$$ A \left ( 1 \right ) = M_{1} $$
$$ A \left ( 2 \right ) = M_{1} A \left ( 1 \right ) + M_{2} $$
$$ A \left ( 3 \right ) = M_{1} A \left ( 2 \right ) + M_{2} A \left ( 1 \right ) + M_{3} $$
$$ A \left ( 4 \right ) = M_{1} A \left ( 3 \right ) + M_{2} A \left ( 2 \right ) + M_{3} A \left ( 1 \right ) + M_{4} $$
$$ A \left ( 5 \right ) = M_{1} A \left ( 4 \right ) + M_{2} A \left ( 3 \right ) + M_{3} A \left ( 2 \right ) + M_{4} A \left ( 1 \right ) + M_{5} $$
그리고 $N > 5$에 대해 다음이 성립함을 쉽게 알 수 있습니다.
$$ A \left ( N \right ) = M_{1} A \left ( N-1 \right ) + M_{2} A \left ( N-2 \right ) + M_{3} A \left ( N-3 \right ) + M_{4} A \left ( N-4 \right ) + M_{5} A \left ( N-5 \right ) $$
아직까지는 복잡도가 $O \left ( N \right )$ 이므로, 행렬 거듭제곱을 활용하여 복잡도를 $O \left ( \log N \right )$ 으로 떨어뜨립니다.
$$ \begin{bmatrix} A \left ( N \right ) \\ A \left ( N-1 \right ) \\ A \left ( N-2 \right ) \\ A \left ( N-3 \right ) \\ A \left ( N-4 \right ) \end{bmatrix} = \begin{bmatrix} M_{1} & M_{2} & M_{3} & M_{4} & M_{5} \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} A \left ( N-1 \right ) \\ A \left ( N-2 \right ) \\ A \left ( N-3 \right ) \\ A \left ( N-4 \right ) \\ A \left ( N-5 \right ) \end{bmatrix} $$
$$ = \begin{bmatrix} M_{1} & M_{2} & M_{3} & M_{4} & M_{5} \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \end{bmatrix}^{N-5} \begin{bmatrix} A \left ( 5 \right ) \\ A \left ( 4 \right ) \\ A \left ( 3 \right ) \\ A \left ( 2 \right ) \\ A \left ( 1 \right ) \end{bmatrix} $$
아래는 이 풀이를 구현한 제 코드입니다.
#include <stdio.h>
#define mod 1000000007
typedef struct {
long long array[5][5];
} Matrix;
Matrix matrix_multiply_modular(Matrix A, Matrix B);
Matrix matrix_power_modular(Matrix A, long long power);
int main() {
long long N, M;
scanf("%lld %lld", &N, &M);
long long count[6] = {};
while(M--)
{
int temp;
scanf("%d", &temp);
count[temp]++;
}
long long simple[6] = {};
for (int i = 1; i <= 5; i++) {
simple[i] = count[i] % mod;
for (int j = 1; j < i; j++) {
simple[i] = (simple[i] + count[j] * simple[i - j]) % mod;
}
}
if (N <= 5) {
printf("%lld", simple[N]);
return 0;
}
Matrix Base = {{{}}};
for (int i = 0; i < 5; i++) {
Base.array[0][i] = count[i + 1];
}
for (int i = 1; i < 5; i++) {
Base.array[i][i-1] = 1;
}
Base = matrix_power_modular(Base, N - 5);
long long Answer = 0;
for (int i = 0; i < 5; i++) {
Answer = (Answer + Base.array[0][i] * simple[5 - i]) % mod;
}
printf("%lld", Answer);
return 0;
}
Matrix matrix_multiply_modular(Matrix A, Matrix B)
{
Matrix result = {{{}}};
for (int i = 0; i < 5; i++) {
for (int k = 0; k < 5; k++) {
for (int j = 0; j < 5; j++) {
result.array[i][j] = (result.array[i][j] + A.array[i][k] * B.array[k][j]) % mod;
}
}
}
return result;
}
Matrix matrix_power_modular (Matrix A, long long power)
{
Matrix result = {{{}}};
for (int i = 0; i < 5; i++) {
result.array[i][i] = 1;
}
while(power) {
if (power % 2) {
result = matrix_multiply_modular(result, A);
}
A = matrix_multiply_modular(A, A);
power /= 2;
}
return result;
}
(C11, 12ms, 1116KB, 제출번호 53526414)
인풋의 크기가 큰 편이므로 Fast I/O를 활용하여 속도를 높일 수 있습니다. (0ms, 제출번호 53526774)
'분할 정복을 이용한 거듭제곱 > 행렬 거듭제곱' 카테고리의 다른 글
백준 24930번: Ordinary Ordinals (0) | 2023.01.08 |
---|---|
백준 13630번: Buses (1) | 2023.01.08 |
백준 11333번: 4×n 타일링 (0) | 2022.05.01 |
백준 17539: Keep Calm and Sell Balloons (0) | 2022.01.20 |
백준 21071번: Long Grid Covering (0) | 2021.12.24 |