求递推公式的第n项,当n很大时,用矩阵快速幂很有效。时间复杂度O(m^3*logn),m为递推公式跨越了几项。
以杭电2017女生专场赛 happy necklace 为例,看如何套板子。
\
分析的递推公式 如图:
\
根据递推公式写出计算an的矩阵,然后套下面板子(计算矩阵乘时,有优化)
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
#define MOD 1000000007
const int m = 3;//m阶矩阵
struct Matrix { //矩阵
long long a[m][m];
Matrix() {
memset(a, 0, sizeof a);
}
Matrix operator * (const Matrix &tmp) { //重载运算符时什么时候加const?
Matrix res = Matrix();
for (int i = 0; i < m; ++i)
for (int k = 0; k < m; ++k)
if (a[i][k] != 0) {
//这种算法,即使想算res.a[0][0]也要把三重循环进行完,
//不同于线性代数老师教的,优点当a[][]=0时,减少计算次数
for (int j = 0; j < m; ++j) {
res.a[i][j] += a[i][k] * tmp.a[k][j];
if (res.a[i][j] >= MOD) res.a[i][j] %= MOD;
}
}
return res;
}
void operator = (const Matrix &tmp) {
memcpy(a, tmp.a, sizeof tmp.a);
}
Matrix operator ^ (long long n) {
Matrix tmp = *(this);
Matrix ans = Matrix();
for (int i = 0; i < m; ++i)
ans.a[i][i] = 1;
while (n) {
if (n & 1) ans = ans * tmp;//若n二进制最低位为1,则乘上x^(x^i)
tmp = tmp * tmp; //模乘
n >>= 1;
}
return ans;
}
};
int main()
{
int t;
long long n;
Matrix T = Matrix();
Matrix A = Matrix();
A.a[0][0] = A.a[0][2] = A.a[1][0] = A.a[2][1] = 1;
scanf("%d", &t);
while (t--) {
scanf("%lld", &n);
T = A;
if (n == 2) puts("3");
else if (n == 3) puts("4");
else {
T = T ^ (n - 3);
long long ans = (4 * T.a[0][0]+ 3 * T.a[0][1] + 2 * T.a[0][2]) % MOD;
printf("%lld\n", ans);
}
}
}
\
其中的快速幂分析,见下图
\
\
\
这不仅仅适用于求第n项,头脑风暴一下,其实其前n项和也是一个道理。
Sn = Sn-1 + f(n)._______f(n)是个递归公式。
希望以后的我,看到这能想起如何使用矩阵快速幂。
\
\