首先,我先介绍一种 O(\(\sqrt n\))的算法
平方分割法
首先,我们先计算如下的式子
F[i]=F[i-2]+F[i-1]=F[i-3]+2F[i-2]=2F[i-4]+3F[i-3]......
这时,我们假设当前为F[i]=a*F[j]+b*F[j-1]
则是不是F[i]=(a+b)*F[j-1]+a*F[j-2
下面就是重点
等等,系数是不是有点眼熟:1,1,2...a,b,a+b
这不正是斐波拉契数列吗? 所以,我们可以得到:F[i]=F[k]F[i-k-1]+F[k+1]F[i-k]
即F[k]F[i]+F[k+1]F[i+1]=F[i+k+1]
F[n+k]=F[k]F[n-1]+F[k+1]F[n]
F[n+k+1]=F[k]F[n]+F[k+1]F[n+1]
F[n+k-1]=F[n+k+1]-F[n+k]
时间复杂度为O(n/k+k),当k=\(\sqrt n\)时取最小,得出结果O(\(\sqrt n\)).
代码:
#include<cstdio> #include<cmath> # define ll long long void make(ll n,ll &x,ll &y,ll m)////x:Fib[n] ; y:Fib[n+1] ; m:Mod { ll a,b,to[3]={0,1,1}; ////特判 if(n<2) { x=to[n]; y=to[n+1]; return; } if(n==2) { x=1; y=2; return; } ////求k,并递归求解Fib[k]及F[k+1] ll k=sqrt(n); make(k,a,b,m); ////从Fib[k]到Fib[k*k]的变化 for(ll i=1;i<=k;++i) { to[2]=(to[1]*a+to[2]*b)%m; to[1]=(to[0]*a+to[1]*b)%m; to[0]=(to[2]-to[1])%m; } k*=k; ////如果已经得到解(n为完全平方数),退出 if(k==n) { x=(to[0]%m+m)%m; y=(to[1]%m+m)%m; return; } ////如果没有,则从k*k迭代到n for(ll i=k+1;i<=n;++i) { to[2]=to[1]; to[1]=(to[0]+to[1])%m; to[0]=to[2]; } x=(to[0]%m+m)%m; y=(to[1]%m+m)%m; } int main() { ll n,x,y; scanf("%lld",&n); make(n,x,y,1000000007); printf("%lld",x); return 0; }
当然这还不够优秀,\(\sqrt n\)只能得到80分,所以还需要引出\(\log_2 n\)的算法。
矩阵加速
个人理解:把递推式转化为矩阵乘法,再利用快速幂。
上代码:
#include<cstdio> #include<iostream> #include<vector> using namespace std; # define Type template<typename T> Type inline T read1() { T t=0; char k=getchar(); bool flag=0; while('0'>k||k>'9') { if(k=='-')flag=1; k=getchar(); } while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^48),k=getchar(); return flag?-t:t; } # define ll long long class Mat { # define Vec vector<ll> # define Arr vector<Vec> Arr a; public: Mat(){} Mat(Arr k):a(k){} Mat(ll x,ll y){a.resize(x);for(ll i=0;i<x;++i)a[i].resize(y);} Vec& operator [](const ll k){return a[k];} ll wide(){return a.size();} ll len(){return a.empty()?0:a[0].size();} Mat operator *(Mat k) { Arr tem; tem.resize(wide()); for(ll i=0;i<wide();++i) { tem[i].resize(k.len()); for(ll j=0;j<len();++j) for(ll l=0;l<k.len();++l) tem[i][l]+=a[i][j]*k[j][l]; } return Mat(tem); } Mat operator +(Mat k) { ll o=max(wide(),k.wide()),p=max(len(),k.len()); Mat tem(o,p); for(ll i=0;i<o;++i) for(ll j=0;j<p;++j) { if(i<wide()&&j<len())tem[i][j]=a[i][j]; if(i<k.wide()&&j<k.len())tem[i][j]+=k[i][j]; } return tem; } Mat operator %(ll k) { Mat tem(wide(),len()); for(ll i=0;i<wide();++i) for(ll j=0;j<len();++j) tem[i][j]=a[i][j]%k; return tem; } Mat& operator %=(ll k){return *this=*this%k;} Mat& operator *=(Mat k){return *this=*this*k;} Mat& operator +=(Mat k){return *this=*this+k;} bool scan(ll x,ll y,const ll value) { if(x>=wide()||y>=len()||x<0||y<0)return 0; a[x][y]=value; return 1; } # undef Vec # undef Arr }; Type T quickpow(T k,const ll n,ll Mod) { if(n==1)return k; T tem=quickpow(k,n>>1,Mod); if(Mod!=0) { tem=(tem*tem)%Mod; if(n&1)tem=(tem*k)%Mod; } else { tem*=tem; if(n&1)tem*=k; } return tem; } # define read read1<ll>() int main() { ll n=read,l=read; if(n==1)return !putchar('1'); Mat k(2,2); k[0][0]=k[0][1]=k[1][0]=1; k=quickpow(k,n-1,l); printf("%lld",k[0][0]); return 0; }