개인적인 난이도
약간 쉬움
알면 풀고 모르면 못푼다
모르겠어서 아이디어 부분을 구글링하고 구현만 내가 했다
복습)
피보나치 수열을 컴퓨터로 표현하는 방법은 여러 가지가 있다.
1. 재귀함수를 이용해 계산한다
2. dp를 이용해 계산한다
3. (오늘 배울 방법) : 행렬의 거듭제곱을 이용해 계산한다.
1. 기본적인 모듈러 연산의 성질을 알아야 한다.
모듈러 연산의 합/차/곱에 대해 교환법칙과 분배법칙이 성립함을 알고 있으면 된다. 입력과 출력 사이의 과정에서 매우 큰 수를 다뤄야 할 때 자주 나온다.
2. 분할정복을 이용한 거듭제곱을 할 줄 알아야 한다
https://www.acmicpc.net/problem/1629
1629번: 곱셈
첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.
www.acmicpc.net
반복되는 연산을 log N 속도로 계산할 수 있다. 코드의 형태가 머리에 잘 안들어가므로 그냥 풀이를 외웠다
3. 피보나치 수를 행렬로 표현할 수 있음을 알아야 한다.
대학교 선형대수학 수업을 한 귀로 듣고 한 귀로 내보냈더니 이걸 몰랐음
피보나치 수열의 n번째 항을 행렬의 거듭제곱 형태로 나타낼 수 있고, 대각화를 통해 일반항까지 구할 수 있다고 한다
위의 동영상을 참고하는 게 이해가 가장 빠르겠다.
그냥 피보나치 수열을 행렬로 나타낼 수 있겠다 ->>> 라는 아이디어로 출발을 한다
An+1
An
이런 2*1짜리 행렬을 생각해보면
이렇게 1*2 행렬과 2*1 행렬의 곱으로 피보나치 수열의 형태를 만들 수 있다
그리고 아래에다가 [1 0] 을 붙이면 깔끔하게 피보나치가 나옴
성공한 코드
#include <bits/stdc++.h>
#define fastio cin.tie(0)->sync_with_stdio(0)
#define ll long long
using namespace std;
const int divnum = 1e9+7;
void mulmat(vector<ll> a,vector<ll> b,vector<ll> &c){
c[0]=(a[0]*b[0])%divnum+(a[1]*b[2])%divnum;
c[1]=(a[0]*b[1])%divnum+(a[1]*b[3])%divnum;
c[2]=(a[2]*b[0])%divnum+(a[3]*b[2])%divnum;
c[3]=(a[2]*b[1])%divnum+(a[3]*b[3])%divnum;
c[0]%=divnum;
c[1]%=divnum;
c[2]%=divnum;
c[3]%=divnum;
}
int main(void) {
fastio;
uint64_t n;cin>>n;
vector<ll> v={1,1,1,0};
vector<ll> ans={1,1,1,0};
n--;
while (n)
{
if(n%2==1){
mulmat(ans,v,ans);
}
n=n>>1;
mulmat(v,v,v);
}
cout<<ans[1]%divnum;
return 0;
}
'C++ > 문제풀이 기록' 카테고리의 다른 글
[BOJ 27502, 쉬움] 가난한 고흐와 붓 (C++) (0) | 2023.02.28 |
---|---|
백준 단계별로 풀어보기 - 시간 복잡도 (24262,24263,24264,24265,24267,24313) (C++) (0) | 2023.02.27 |
[BOJ 25907, 쉬움] 양과 늑대 (C++) (0) | 2023.02.02 |
[BOJ 27296, 약간 어려움] 카탈란 마스터의 선분 그리기 게임 (C++) (1) | 2023.02.02 |
[BOJ 1799, 약간 어려움] 비숍 (C++) (0) | 2023.01.26 |