这个题其实很简单,简单分析一下规律,发现发f[i]=f[i-1]+f[i-2]。
如下图:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int n,i,j,a[101]; 6 cin>>n; 7 a[1]=1;a[2]=2; 8 for (i=3;i<=n;i++) 9 { 10 a[i]=a[i-1]+a[i-2]; 11 } 12 cout<<a[n]; 13 }
|
用这个代码,解决这个题的确很轻松。
可是只要稍微更改一下数据范围,就完全不一样了:
这样子,难度就完全不是一个等级了。
首先是不能开一个10^18^的数组,那样肯定会爆内存。
我们可以用滚动的数组:
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
| #include<bits/stdc++.h> #define mod 1e9+7
using namespace std;
long long a[4] = {0,1,2};
int main() { freopen("brick.in", "r", stdin); freopen("brick.out", "w", stdout); int fbk; cin >> fbk; if (fbk==1) { cout << 1; } else if (fbk==2) { cout << 2; } else { for (int yousa = 3; yousa <= fbk-2; yousa++) { a[3] = (a[2] + a[1])%(long long)(mod); a[1] = a[2]; a[2] = a[3]; } cout << a[3] << "\n"; } fclose(stdin); fclose(stdout); }
|
测试之后发现虽然内存占用很少,但是会超时。
这就用到我们的矩阵加速了:
矩阵乘法
矩阵乘法可以先稍作了解,知道矩阵相乘的运算法则
快速幂
快速幂要求解的是这样一类问题:
给你A,B,C,求A的B次方模C的余数
A,C<=10^9^,B<=10^18^
如果我们线性去求,时间复杂度是O(n)的,但题目中给出的B是很大的数,这样显然会超时,我们可以用快速幂来加速这个过程。
我们可以想像一下小学的时候我们如何计算2^16^
2^16^=4^8^=16^4^=256^2^=65536
那如何计算2^18^呢?
快速幂同理也是如此
我们可以按照上面做法,利用分治的思想求去解
这样原本O(n)的时间复杂度便降到了O(log n )
1 2 3 4 5 6 7 8 9 10
| long long ans=1,base=a; while(n>0) { if(n&1) { ans*=base; } base*=base; n=n/2; }
|
矩阵快速幂
矩阵快速幂的原理同快速幂一样,只是转换为了矩阵之间的乘法操作
所以单纯的重载一下运算符(写成函数的形式也可),将普通的乘法转换为矩阵乘法就好了。
矩阵加速
知道那个叫矩阵快速幂的东西后我们可以学矩阵加速了
斐波那契数列中的每一项都是前两项之和
我们考虑构造这么一个矩阵:每一次乘上这个矩阵都能从f[n-1],f[n-2]两项向后递推到f[n-1],f[n]这两项
那么关键就是如何构造这样的矩阵
对于这样一个矩阵我们有
所以我们将每一次两项相加转换为了乘以一个转移矩阵
既然是乘法,每次乘以的也是同一个矩阵
我们可以利用矩阵快速幂的思想对于求解斐波那契数列加速
代码实现基本上是一致的,只需要构造一个转移矩阵来进行状态之间的转移即可
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
| struct mat{ ll m[5][5]; }a,ans; ll n,b,k; mat mul(mat x,mat y,int flag){ mat c; for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) c.m[i][j]=0; for(int i=1;i<=2;i++){ for(int j=1;j<=2;j++){ for(int q=1;q<=2;q++){ c.m[i][j]=(c.m[i][j]+x.m[i][q]*y.m[q][j])%Mod; } } } return c; } int main(){ cin >> n; a.m[1][1]=1;a.m[1][2]=1; a.m[2][1]=1;a.m[2][2]=0; b=n-2; ans.m[1][1]=1; ans.m[1][2]=1; while(b){ if(b&1){ ans=mul(ans,a,1); } a=mul(a,a,2); b=b/2; } if(n==1||n==2)cout<<1; else cout<<ans.m[1][1]%Mod; }
|
这样这个题就被解决了!