알고리즘 기술

피보나치 수 를 log2(N) 빠르기로 구하기.

헐랭미 2020. 6. 12. 14:36

일반적으로 구하는 피보나치 수는 O(N)이다. 

그런데

 

https://www.acmicpc.net/problem/2749

 

2749번: 피보나치 수 3

첫째 줄에 n이 주어진다. n은 1,000,000,000,000,000,000보다 작거나 같은 자연수이다.

www.acmicpc.net

n이 1,000,000,000,000,000,000이따구면 어떻게 풂?

 

 

행렬 곱셈을 이용해서 빠르게 구해보자.

일단 F0 = 0 F1 = 1 F2 = 1 F3 = 2 F4 = 3 ........이다.

피보나치의 행렬 곱셈은 이 두 식이 핵 중요하다.

 

그러면 F13 은 어떻게 구하냐?

1. 13을 1까지 쪼갠다.

 

13 -> 12 -> 6 -> 3 -> 2 -> 1

    -1     /2     /2    -1   -1

 

2. 초기 행렬식인

부터 시작해 
기존에 구한 -1 -> /2 -> /2 -> -1 -> -1 을 거꾸로 해주면 된다.

즉, +1 -> +1 -> *2 -> *2 -> +1 이렇게 말이다.

 

이제 2749번을 풀어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <vector>
#include <algorithm>
#define ll long long int
#define MOD 1000000
using namespace std;
 
 
int arr[100000];
int arr_size = 0;
 
void flow(ll n)
{
    if (n == 1)
        return;
 
    if (n % 2 == 0)
    {
        arr[arr_size++= 2;
        flow(n / 2);
    }
    else
    {
        arr[arr_size++= 1;
        flow(n - 1);
    }
}
 
ll go(int cnt, ll a, ll b, ll c, ll d)
{
    if (cnt == -1)
        return b;
 
    if (arr[cnt] == 1// +
        return go(cnt - 1, (a + b) % MOD, a, (c + d) % MOD, c);
    else
        return go(cnt - 1, (a * a + b * c) % MOD, (a * b + b * d) % MOD, (a * c + c * d) % MOD, (b * c + d * d) % MOD);
}
 
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
 
    ll n;
    cin >> n;
    flow(n);
 
    cout << go(arr_size-11110);
 
}
cs

 

 

피사노주기로 푸는 방법이 있긴 한데 머리의 부족함으로 나중에 보자;;;;