M斐波那契數列 (費馬小定理 + 二分快速冪 + 矩陣快速冪)
阿新 • • 發佈:2019-02-08
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;
}