令F(i)表示斐波那契数列的第i项。
S1(i)表示F数列的前i项和。
S2(i)表示S1数列的前i项和。
S3(i)表示S2数列的前i项和。
S4(i)表示S3数列的前i项和。
S5(i)表示S4数列的前i项和。
S6(i)表示S5数列的前i项和。
S7(i)表示S6数列的前i项和。
S8(i)表示S7数列的前i项和。
求S8(N)。答案可能很大,请对1000000007取模。
斐波那契数列的定义可以查看https://baike.baidu.com/item/斐波那契数列
多组数据。
输入的第一行包括一个整数表示数据组数T(1<=T<=1000)。
每组数据的第一行包括一个整数表示题目所说的N(1<=N<=1000000000000000000)。
每组数据在单独的一行中输出S8(N)。
Sample Input
4
1
2
3
4
Sample Output
1
9
46
175
F数列前四项:1 1 2 3
S1数列前四项:1 2 4 7
S2数列前四项:1 3 7 14
S3数列前四项:1 4 11 25
S4数列前四项:1 5 16 41
S5数列前四项:1 6 22 63
S6数列前四项:1 7 29 92
S7数列前四项:1 8 37 129
S8数列前四项:1 9 46 175
题目不难理解,但是N取到1000000000000000000
如果用迭代、递归,做法很简单,但是肯定是超时的。
所以这个方法行不通 那么应该怎么入手?
我们应该想到 求斐波那契数列可以用矩阵乘法来求,我们构造一个21的矩阵M[i]={ {F(i-1)},{F(i)}},再构造一个22的矩阵A={ {0,1},{1,1}},根据矩阵乘法如果M[i]左乘上A,结果是一个21的矩阵{ {F[i]},{F[i]+F[i-1]}}即{ {F[i]},{F[i+1]}},我们记为矩阵M[i+1]。很明显,M[i]左乘j次A,结果就是M[i+j]。
S1(i+1)=S1(i)+F(i+1)=S1(i)+F(i)+F(i-1)。
S2(i+1)=S2(i)+S1(i+1)=S2(i)+S1(i)+F(i+1)=S2(i)+S1(i)+F(i)+F(i-1)。
以此类推。
可以想象,如果我们构造一个101的矩阵M[i]={ {F(i-1)},{F(i)},{S1(i)},{S2(i)},{S3(i)},{S4(i)},{S5(i)},{S6(i)},{S7(i)},{S8(i)}},也会存在一个1010的矩阵A令AM[i]=M[i+1]。
这样我们可以通过构造M[1]和A,让M[1]左乘N-1次A求得M[N],M[N]的最后一行就是S8(N)。这样做的时间复杂度是o(100N),不用我说你们也知道会超时了,这甚至比暴力计算还满。
但是矩阵乘法是满足结合律的(圈起来,这个要考)。我们可以将A的N-1次方进行二进制分解,比如A7=A4A2*A,如果我们事先求得矩阵A的1次方(即A本身)、2次方、4次方、8次方、16次方……我们就可以通过将这些矩阵中的一部分相乘求出A(N-1)。很容易看出来N最多分解为log2(N)项。所以时间复杂度就变成了o(1000log2(N))。这里的1000表示的是1010的两个方阵相乘的复杂度,而之前的100指的是101的矩阵左乘1010的矩阵。
时间复杂度o(T1000log2(N)),空间复杂度o(100)。
以上内容是官方题解
本人在题解和小伙伴的想法下自己做了一遍
我的基础矩阵与上文不同 所以构造的M矩阵也有点不同
虽然有些粗糙 但是结果是一样的
我们的基础矩阵是 1X10 但是为了方便代码运算 我们用0 将其补充到10X10 在乘法运算中 0的列对结果不会有任何影响
下面给出推导过程:
为了做这题 提前预习了一波
后天的课才开始讲矩阵的乘法
以下为代码实现
#include
using namespace std;
typedef long long ll;
ll p = 1000000007;
struct Mat {
ll a[10][10] ;
Mat () {
memset(a,0,sizeof(a)); //初始化a数组为0
}
};
Mat mul(Mat a,Mat b) // 矩阵的乘法
{
Mat res = Mat();
for(ll i = 0;i < 10;i++)
for(ll j = 0;j < 10;j++)
for(ll k = 0;k < 10;k++)
{
if(a.a[i][k] && b.a[k][j])
{
res.a[i][j] += (a.a[i][k] % p) * (b.a[k][j]%p) %p;
}
}
return res;
}
Mat ksm(Mat n,ll k)
{
Mat ans = Mat();
for(ll i = 0;i < 10;i++) //初始化ans为单位矩阵
ans.a[i][i] = 1;
while(k)
{
if(k&1) ans = mul(ans,n);
n = mul(n,n);
k >>= 1;
}
return ans;
}
int main()
{
ll f1[10][10] = { //每行首个1代表 fib(2)fib(1) S1(2) S2(2) ... S8(2)
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0},
{2,0,0,0,0,0,0,0,0,0},
{3,0,0,0,0,0,0,0,0,0},
{4,0,0,0,0,0,0,0,0,0},
{5,0,0,0,0,0,0,0,0,0},
{6,0,0,0,0,0,0,0,0,0},
{7,0,0,0,0,0,0,0,0,0},
{8,0,0,0,0,0,0,0,0,0},
{9,0,0,0,0,0,0,0,0,0}
};
ll xishu[10][10] = { // 系数矩阵 f1矩阵乘该矩阵的n次方则可以得到fib(2+n) fib(1+n) S1(2+n) ... S8(2+n)
{1,1,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,0},
{1,1,1,1,1,1,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0},
{1,1,1,1,1,1,1,1,0,0},
{1,1,1,1,1,1,1,1,1,0},
{1,1,1,1,1,1,1,1,1,1}
} ;
ll t ;
cin >>t;
while(t--)
{
ll n;
cin >> n;
if(n < 2)
{
cout << 1 << endl;
continue;
}
Mat a = Mat();
memcpy(a.a ,f1,sizeof(f1));
Mat xib = Mat();
memcpy(xib.a ,xishu,sizeof(xishu));
Mat ans = Mat();
ans = mul(ksm(xib,n-2),a);
cout << ans.a[9][0]%p << endl;
}
return 0;
}