피보나치 수열 행렬 곱셈 최적화 DP

1597 단어

피보나치 수열 행렬 곱셈 최적화 DP


\(f (n)\%1000000007\),\(n\le 10^ {18}\)
행렬 곱하기:\(i\times k\)의 행렬\(A\) 곱하기\(k\times j\)의 행렬\(B\)는\(k\times k\)의 행렬을 받습니다. 그 중에서 제\(i\) 열 제\(j\) 줄의 수는\(A\)의 제\(i\) 줄의 모든 수와\(B\)의 제\(j\) 열을 각각 곱하고 덧붙입니다.
매트릭스 곱셈을 사용하여 DP를 최적화하는 것을 고려하여\(f(n)\)를 얻기 위해 매트릭스\(\text{base}\)를 설정하여\(\begin{bmatrix} F {n-1} & F {n-2}\end{bmatrix}\times base =\begin{bmatrix} F {n} & F {n-1}\end{bmatrix}\)를 쉽게 추측할 수 있도록\(base=\begin{bmatrix} {bmat1} &\\{bmatrix}\1 &\{bmandx} {bmatrix}\1
그리고 행렬 곱셈은 빠른 멱 최적화를 사용할 수 있기 때문에\(base^{n-2}\)를 직접 계산합니다. 답은 $\begin{bmatrix} 1 & 1\end{bmatrix}\times base^{n-2} $행렬의 첫 번째 줄 첫 번째 열입니다.
#include 
#include 
#define MOD 1000000007
using namespace std;
long long n;
struct Mat{
    long long a[3][3];
    Mat(){memset(a, 0, sizeof a);}
    Mat operator * (const Mat &b) const{
        Mat res;
        for(int i=1;i<=2;++i)
            for(int j=1;j<=2;++j)
                for(int k=1;k<=2;++k)
                    res.a[i][j]=(res.a[i][j] + (a[i][k] * b.a[k][j])%MOD)%MOD;
        return res;
    }
} ans, base;
void qpow(long long x){
    while(x!=0){
        if(x&1) ans=ans*base;
        x>>=1;
        base=base*base;
    }
}
int main(){
    scanf("%lld", &n);
    if(n<=2){
        puts("1");
        return 0;
    }
    ans.a[1][1]=ans.a[1][2]=1;
    base.a[1][1]=1,base.a[1][2]=1,base.a[2][1]=1,base.a[2][2]=0;
    qpow(n-2);
    printf("%lld", ans.a[1][1]);
    return 0;
}

좋은 웹페이지 즐겨찾기