@Gary-Ying
2018-08-08T04:32:03.000000Z
字数 3992
阅读 1552
解题报告 算法
题目大意:计算数列a的第n项,其中:
一般的递推是O(n)的,显然时间和空间都不能承受。
由于每一步递推都是相同的。这句话包含了2个层面:首先,递推式是相同的;其次,递推的条件也要是相同的。综合来说,每一步的递推都是相同的。这是应用矩阵加速递推的充分条件。
那么怎么进行矩阵加速呢?我们首先观察,第项和哪些项有关? 与和优化,也就是和前3项有关。为了能够**仅仅利用矩阵就能推出, 我们需要记录前3项,于是,我们构造一个3*3的矩阵:
我们首先要构造出类似于DP的状态来存下所有计算过程中可能会用到的信息,对于这道题,我们需要记录:(假设我们要从推到)
那好,我们已经得到:
由于,而根据矩阵,,所以,矩阵的第一行是,以此类推,就可以吧矩阵填满了。
然后,我们可以得到:
我们知道:,如果把它们代入矩阵(就是中间的那个矩阵),我们会得到:
在mod p意义下?矩阵乘法满足结合律?
快速幂!
为什么可以用快速幂这个黑科技呢?
普通的快速幂是用来求的,其原理是把二进制拆分成,从而得到 ,只要满足乘法结合律,就可以进行快速幂。
矩阵快速幂通常用来加速递推。比如说斐波那契数列的第n项mod p的值也可以用矩阵快速幂来求。但是并不是所有的递推都可以用矩阵快速幂,只有那些转移具有周期性的递推才能使用。
1、矩阵的定义(结构体)
struct Square{int mat[3][3];void clear(){memset(mat, 0, sizeof(mat));}} Base, Result;
2、方阵的乘法
void Times(Square &A, Square B){Square C; C.clear();for (int i = 0; i <= 2; ++i)for (int j = 0; j <= 2; ++j)for (int k = 0; k <= 2; ++k)(C.mat[i][j] += (LL)A.mat[i][k] * B.mat[k][j] % p) %= p;A = C;}
3、矩阵快速幂
void SquareQpow(Square Base, int k){if (k == 1){Result = Base;return;}Result.clear();SquareQpow(Base, k / 2);Times(Result, Result);if (k % 2 == 1) Times(Result, Base);}
4、矩阵初始化
void init(){Base.clear();Base.mat[0][0] = 1; Base.mat[0][2] = 1;Base.mat[1][0] = 1; Base.mat[2][1] = 1;}
(C.mat[i][j] += (LL)A.mat[i][k] * B.mat[k][j] % p) %= p;
#include<iostream>#include<cstdio>#include<cstring>#define LL long longusing namespace std;const int p = 1e9 + 7;struct Square{int mat[3][3];void clear(){memset(mat, 0, sizeof(mat));}} Base, Result;void init(){Base.clear();Base.mat[0][0] = 1; Base.mat[0][2] = 1;Base.mat[1][0] = 1; Base.mat[2][1] = 1;}void Times(Square &A, Square B){Square C; C.clear();for (int i = 0; i <= 2; ++i)for (int j = 0; j <= 2; ++j)for (int k = 0; k <= 2; ++k)(C.mat[i][j] += (LL)A.mat[i][k] * B.mat[k][j] % p) %= p;A = C;}void SquareQpow(Square Base, int k){if (k == 1){Result = Base;return;}Result.clear();SquareQpow(Base, k / 2);Times(Result, Result);if (k % 2 == 1) Times(Result, Base);}int main(){int T; scanf("%d", &T);while (T--){int n;scanf("%d", &n);if (n <= 3) printf("1\n");else{init();SquareQpow(Base, n-3);printf("%d\n", ((Result.mat[0][0] + Result.mat[0][1]) % p + Result.mat[0][2]) % p);}}return 0;}