重邮2018acm校选赛 G超级斐波那契数列 矩阵快速幂求斐波那契数列第1e18项的第8重求和

问题描述

令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)。
以此类推。
可以想象,如果我们构造一个10
1的矩阵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=A4
A2*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的列对结果不会有任何影响
下面给出推导过程:

重邮2018acm校选赛 G超级斐波那契数列 矩阵快速幂求斐波那契数列第1e18项的第8重求和_第1张图片

为了做这题 提前预习了一波
后天的课才开始讲矩阵的乘法

以下为代码实现

#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;
	
	
	
 } 





你可能感兴趣的:(ACM,ACM)