https://www.acmicpc.net/problem/11444
11444번: 피보나치 수 6
첫째 줄에 n이 주어진다. n은 1,000,000,000,000,000,000보다 작거나 같은 자연수이다.
www.acmicpc.net
문제 개요
백준 10830번: 행렬 제곱 문제를 응용해서 푸는 문제이다.
코드 자체도 비슷하고, 분할 정복 방법도 똑같은 문제이기에 한 번 볼 필요가 있다.
먼저 피보나치와 행렬 간에 어떤 유사점이 있는지 살펴보면
$$ \begin{pmatrix}
0 & 1 \\
1 & 1 \\
\end{pmatrix} ^{1} = \begin{pmatrix}
F_{n-1} & F_{n} \\
F_{n} & F_{n+1} \\
\end{pmatrix} ^{n} $$
이러한 관계를 가지고 있다.
n 행렬의 제곱이 n+1 행렬이 되는 것을 볼 수 있다.
$$ \begin{pmatrix}
0 & 1 \\
1 & 1 \\
\end{pmatrix} ^{1} \times \begin{pmatrix}
0 & 1 \\
1 & 1 \\
\end{pmatrix} ^{1} = \begin{pmatrix}
1 & 1 \\
1 & 2 \\
\end{pmatrix} ^{2} $$
n이 홀수일 경우에도 앞선 문제와 같이
n-1 행렬과 n=1 행렬을 곱해주면 된다.
코드
입력값 n이 큰 값이기에 long long으로 선언하고, 벡터 또한 long long으로 선언한 모습을 볼 수 있다.
값이 많이 크기에 곱셈 부분에서 행렬의 값을 따로 1,000,000,007의 나머지로 계산했다.
#include <bits/stdc++.h>
using namespace std;
long long n;
// matrix = 피보나치 n=1, temp = 제곱한 피보나치 행렬
vector matrix(2, vector<long long>(2, 0));
vector<vector<long long> > temp;
//행렬의 곱셈
vector<vector<long long> > matrix_mult(vector<vector<long long> >m_1, vector<vector<long long> > m_2) {
vector result(2, vector<long long>(2, 0));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
result[i][j] += (m_1[i][k] % 1000000007) * (m_2[k][j] % 1000000007);
}
}
}
return result;
}
// 거듭제곱 재귀이용
vector<vector<long long> > pow(long long m) {
if (m == 1) return matrix;
//홀수일 때
if (m % 2 > 0) return matrix_mult(pow(m-1), matrix);
vector<vector<long long>> pow_m = pow(m / 2);
return matrix_mult(pow_m, pow_m);
}
int main(void) {
cin >> n;
matrix[0][0] = 0, matrix[0][1] = 1,
matrix[1][0] = 1, matrix[1][1] = 1;
temp = pow(n);
cout << temp[0][1] % 1000000007;
}'알고리즘 > 분할 정복' 카테고리의 다른 글
| (c++) BOJ 11401번: 이항 계수 3 (1) | 2024.01.14 |
|---|---|
| (c++) BOJ 1629번: 곱셈 (0) | 2024.01.13 |
| (c++) BOJ 1780번: 종이의 개수 (1) | 2024.01.13 |
| (c++) BOJ 2630번: 색종이 만들기 (0) | 2024.01.12 |
| (c++) BOJ 10830번: 행렬 제곱 (0) | 2024.01.11 |
댓글