1. 程式人生 > >M斐波那契數列 (費馬小定理 + 二分快速冪 + 矩陣快速冪)

M斐波那契數列 (費馬小定理 + 二分快速冪 + 矩陣快速冪)

 M斐波那契數列F[n]是一種整數數列,它的定義如下:

F[0] = a
F[1] = b
F[n] = F[n-1] * F[n-2] ( n > 1 )

現在給出a, b, n,你能求出F[n]的值嗎? 

Input
輸入包含多組測試資料;
每組資料佔一行,包含3個整數a, b, n( 0 <= a, b, n <= 10^9 )
Output
對每組測試資料請輸出一個整數F[n],由於F[n]可能很大,你只需輸出F[n]對1000000007取模後的值即可,每組資料輸出一行。
Sample Input

0 1 0
6 10 2

Sample Output

0
60

題解

F(0)=a,F(1)=b
F(n)=F(n−1)F(n−2)
⇒F(n)=F(n−2)2F(n−3)
⇒F(n)=F(n−3)3F(n−4)2
⇒F(n)=F(n−4)5F(n−5)3

⇒F(n)=F(1)f(n)F(0)f(n−1)
⇒F(n)=bf(n)af(n−1)
f(n)正是斐波那契數列。
矩陣快速冪可以求出f(n),f(n−1)的值。
然後快速冪計算bf(n),af(n−1), 答案就是兩者乘積。
需要注意一點,取模是對F(n)取模,不是f(n),那麼問題來了,f(n)會是一個很大的數,如果不模一下根本存不下,怎麼辦呢?
這裡的模 p=1000000007,是個素數,由尤拉定理,
a^x≡a^(x%(p-1))(mod p)
所以f(n)可以對 p−1取模。
斐波那契數列就用矩陣快速冪去求:

最後再快速冪取模

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1000000007;
typedef long long LL;
LL a,b;
int n;
LL pow(LL a,LL b){
    LL res=1;
    while(b){
        if(b&1)
            res=(res*a)%mod;
            a=(a*a
)%mod; b>>=1; } return res; } LL mul(int n){ LL t[2][2]={1,1,1,0}; LL ans[2][2]={1,0,0,1}; LL temp[2][2]; while(n){ if (n&1){ for(int i=0;i<2;i++) for(int j=0;j<2;j++) temp[i][j]=ans[i][j],ans[i][j]=0; for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) ans[i][j]=(ans[i][j]+temp[i][k]*t[k][j])%(mod-1); } for(int i=0;i<2;i++) for(int j=0;j<2;j++){ temp[i][j]=t[i][j]; t[i][j]=0; } for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) t[i][j]=(t[i][j]+temp[i][k]*temp[k][j])%(mod-1); n>>=1; } return (pow(a,ans[1][1])*pow(b,ans[1][0]))%mod; } int main() { while(scanf("%lld%lld%d",&a,&b,&n)!=EOF){ if(n==0) printf("%lld\n",a%mod); else if(n==1) printf("%lld\n",b%mod); else printf("%lld\n",mul(n)); } return 0; }