这里我们以Fib问题为例。
Fib数列是一个著名的递推数列,其规律可表示为f[i]=f[i-1]+f[i-2];
我们要做的事,就是求出fib数列的第n项。由于这个数字可能很大,因此只需要输出它对10007取模的结果。
这里我们约定:对于80%的数据,n<10^8。对于100%的数据,n<10^10。
几乎不需要经过多少思考,我们就能写出一个80分版本:
#include <cstdio>
using namespace std;
int main(){
freopen("fib.in","r",stdin);
freopen("fib.out","w",stdout);
int t,a=1,b=1,c;
scanf("%d",&t);
for(t-=2;t>0;--t)
b+=a, a=b-a, a%=100000000, b%=100000000;
printf("%d\n",b%10007);
}
然而,仁慈的80分并不能阻止我们的进一步思考。
我们知道,递推的规律可以表示为一个矩阵:{{0,1},{1,1}}
我们采用矩阵乘法的方法,套上qpow的外壳,就形成了以下的100分版本:
#include <cstdio>
using namespace std;
struct martix{
long long a[2][2];
martix(){
a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;
}
void getmod(){
for(int i=0;i<=1;i++)
for(int j=0;j<=1;j++)
a[i][j]%=100000000;
return;
}
};
martix mui(martix a,martix b){
martix r;
for(int i=0;i<=1;i++)
for(int j=0;j<=1;j++)
for(int k=0;k<=1;k++)
r.a[i][j]+=a.a[i][k]*b.a[k][j];
return r;
}
martix reset(int x,int y,int z,int w){
martix p;
p.a[0][0]=x;p.a[0][1]=y;
p.a[1][0]=z;p.a[1][1]=w;
return p;
}
int main(){
freopen("fib.in","r",stdin);
freopen("fib.out","w",stdout);
martix base,result;
int t;
scanf("%d",&t);
base=reset(0,1,1,1);
result=reset(1,1,1,1);
t=t>2?t-2:0;
while(t){
if(t&1) result=mui(result,base);
base=mui(base,base);
base.getmod();
result.getmod();
t>>=1;
}
printf("%d\n",result.a[1][1]%10007);
}
看起来还真是有点麻烦,不过仔细想想,原理也很简单,只是手指要多费点功夫罢了。