斐波拉契数列是大家所熟悉的,至于怎样求斐波拉契数列的第N项相信大家都知道。
我们用 sum 来表示前缀和
sumn=∑ni=1fi , fi 表示斐波拉契数列的第i项。
显然 sumn=sumn−1+fn
我们进一步化简,
尝试将它化简成只与 sum 的式子
sumn=sumn−1+fn−1+fn−2
看起来现在好像没有办法,
但其实可以接着化简。
sumn=2∗sumn−1−sumn−3
但是, x,y 非常大,我们考虑矩阵乘法。
因为只与sum的前三项有关,所以我们就用一个 1∗3 的矩阵×一个 3∗3 的矩阵
一个 1∗3 的矩阵就是 [sumn−3,sumn−2,sumn−1]
而得到的结果也是一个 1∗3 的矩阵,
这个矩阵就是 [sumn−2,sumn−1,sumn]
根据矩阵乘法的原理,
我们就可以推出中间的转移矩阵。
因为矩阵乘法满足乘法结合律,所以可以使用快速幂。
最终的时间复杂度 O((logx+logy)∗T) ,是可以通过的。
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define mo %10000
using namespace std;
typedef int arr [3][3];
int sta[3]={1,2,4};
int ans[3];
int n,x,y,ans1,ans2,ans3;
arr sum;
void ksm(int x)
{
arr s,t;
memset(sum,0,sizeof(sum));
memset(s,0,sizeof(s));
s[1][0]=1;
s[2][1]=1;
s[0][2]=-1;
s[2][2]=2;
if(x)
{
sum[1][0]=1;
sum[2][1]=1;
sum[0][2]=-1;
sum[2][2]=2;
x--;
}
while(x)
{
if(x%2)
{
memcpy(t,sum,sizeof(t));
memset(sum,0,sizeof(sum));
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
sum[i][j]=(sum[i][j]+(s[i][k]*t[k][j])mo)mo;
}
memcpy(t,s,sizeof(t));
memset(s,0,sizeof(s));
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
s[i][j]=(s[i][j]+(t[i][k]*t[k][j])mo)mo;
x=x/2;
}
}
int main()
{
scanf("%d",&n);
for(int k=1;k<=n;k++)
{
scanf("%d%d",&x,&y);
if(x>4)
{
ksm(x-4);
memset(ans,0,sizeof(ans));
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
ans[i]=(ans[i]+(sta[j]*sum[j][i])mo)mo;
ans1=ans[2];
}
else
{
if(x==1)ans1=0;else ans1=sta[x-2];
}
if(y>3)
{
ksm(y-3);
memset(ans,0,sizeof(ans));
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
ans[i]=(ans[i]+(sta[j]*sum[j][i])mo)mo;
ans2=ans[2];
}
else ans2=sta[y-1];
ans3=ans2-ans1;
while(ans3<0)ans3+=10000;
ans3=ans3 mo;
printf("%d\n",ans3);
}
}