链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1126
矩阵的快速幂是用来高效地计算矩阵的高次方的。将朴素的o(n)的时间复杂度,降到
log(n)。这里先对原理(主要运用了矩阵乘法的结合律)做下简单形象的介绍:一般
一个矩阵的n次方,我们会通过连乘n-1次来得到它的n次幂。但做下简单的改进就能减
少连乘的次数,方法如下:
把n个矩阵进行两两分组,比如:A*A*A*A*A*A => (A*A)*(A*A)*(A*A)这
样变的好处是,你只需要计算一次A*A,然后将结果(A*A)连乘自己两次就能到A^6,
即(A*A)^3=A^6。算一下发现这次一共乘了3次,少于原来的5次。
其实大家还可以取A^3作为一个基本单位。原理都一样:利用矩阵乘法的结合律,来减
少重复计算的次数。
以上都是取一个具体的数来作为最小单位的长度,这样做虽然能够改进效率,但缺陷也
是很明显的,取个极限的例子(可能有点不恰当,但基本能说明问题),当n无穷大的
时候,你现在所取的长度其实和1没什么区别。所以就需要我们找到一种与n增长速
度”相适应“的”单位长度“,那这个长度到底怎么去取呢???这点是我们要思考的问
题。
有了以上的知识,我们现在再来看看,到底怎么迅速地求得矩阵的N次幂。
代码如下:
#include <stdio.h>
#define M 1000000007
int f[3] = {2, 1, 1};
void matrix(unsigned long long a[3][3], unsigned long long b[3][3], unsigned
long long m) {
unsigned long long c[3][3];
int i, j, k;
for (i=0; i<3; ++i) {
for (j=0; j<3; ++j) {
c[i][j] = 0;
for (k=0; k<3; ++k) {
c[i][j] += a[i][k] * b[k][j] % m;
}
c[i][j] %= m;
}
}
for (i=0; i<3; ++i) {
for (j=0; j<3; ++j) a[i][j] = c[i][j];
}
}
unsigned long long exp(unsigned long long cs[3][3], unsigned long long
s[3][3], unsigned long long n, unsigned long long m) {
if (n < 3) return f[2-n];
n -= 2;
while (n) {
if (n & 1) matrix(cs, s, m);
n >>= 1;
matrix(s, s, m);
}
unsigned long long ans = 0;
for (int i=0; i<3; ++i) ans += cs[0][i] * f[i] % m;
return ans % m;
}
int main() {
int t, c;
scanf ("%d", &t);
c = 0;
while (t--) {
int n;
scanf ("%d", &n);
unsigned long long s[3][3] = {
{1, 1, 1},
{1, 0, 0},
{0, 1, 0},
};
unsigned long long cs[3][3] = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
};
unsigned long long ans = exp(cs, s, n, M);
printf ("Case %d:\n", ++c);
printf ("%llu\n", ans);
}
return 0;
}
如有问题,欢迎交流!