这篇文章上次修改于 366 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
ACM程序课算法笔记19——矩阵快速幂
矩阵快速幂是对递推式的一种高效解决方法。
主要的解决问题是类似于斐波那契数列 f(n) = f(n - 1) + f(n - 2) 的使用。
不过我们要先进行矩阵基本运算在代码中的体现。
矩阵运算
struct mat {
LL a[sz][sz];
mat() { memset(a, 0, sizeof a); }
mat operator-(const mat& T) const {
mat res;
for (int i = 0; i < sz; ++i)
for (int j = 0; j < sz; ++j) {
res.a[i][j] = (a[i][j] - T.a[i][j]) % MOD;
}
return res;
}
mat operator+(const mat& T) const {
mat res;
for (int i = 0; i < sz; ++i)
for (int j = 0; j < sz; ++j) {
res.a[i][j] = (a[i][j] + T.a[i][j]) % MOD;
}
return res;
}
mat operator*(const mat& T) const {
mat res;
int r;
for (int i = 0; i < sz; ++i)
for (int k = 0; k < sz; ++k) {
r = a[i][k];
for (int j = 0; j < sz; ++j)
res.a[i][j] += T.a[k][j] * r, res.a[i][j] %= MOD;
}
return res;
}
mat operator^(LL x) const {
mat res, bas;
for (int i = 0; i < sz; ++i) res.a[i][i] = 1;
for (int i = 0; i < sz; ++i)
for (int j = 0; j < sz; ++j) bas.a[i][j] = a[i][j] % MOD;
while (x) {
if (x & 1) res = res * bas;
bas = bas * bas;
x >>= 1;
}
return res;
}
};
上面的矩阵乘法运算是优于下面这个的,优于成块空间的优化导致。
mat operator*(const mat& T) const {
mat res;
for (int i = 0; i < sz; ++i)
for (int j = 0; j < sz; ++j)
for (int k = 0; k < sz; ++k) {
res.a[i][j] += mul(a[i][k], T.a[k][j]);
res.a[i][j] %= MOD;
}
return res;
}
问题提出
我们希望在代码角度解决类似于斐波那契数列求第n项的问题
可得
求an项的通项公式
递推公式和矩阵思想
对于以上问题,我们就需要使用矩阵的思想,构建以下矩阵
此时,我们可以发现其中的间接矩阵是我们构建的,并且能让式子成立,并且这就是一个矩阵的递推公式,是一个乘积的形式,每项也就变成了幂次这个间接矩阵。
首项 也就是 1 1了。
拆解这个东西我们就可以发现
这是一个构造的实际例子。为了更便于后序的理解和方法总结,以下再给出6个题目尝试去构造。
矩阵构造
练习一
求f(n),并且其中有以下递推式
构造一
对于本题,f(n)借力于f(n - 3) f(n - 4),不妨我们将它们都直入于矩阵
矩阵的构造实际上就是为前一项的多个式能够配到后一项。
可以得到以上矩阵式,我们可以发现,为了让f(n)能够顺利的获取到前面的项,我们就保存了它们,并且让它们出现在前一项。
需要注意的是,我们使用了f(n - 2)它看似无用,实际上是为了保证中间能顺利递推下去。
总结为:它需要什么,我们就存什么
练习二
求f(n),并且其中有以下递推式
构造二
对于本体,引入了一个常数,这无疑干扰和打破了我们原来计算的规律,但是常数终究是常数,按照构造一的结论,因为需要c,我们就把c一起存起来就好了
以上构造式中,最后一行是c的构造,可以发现 0 0 0 1是一种恒定不变的形式,顺利达成。
练习三
求f(n),并且其中有以下递推式
其中f(1) = 1 f(2) = 2
构造三
该构造的特点就是,引入的常量变成了一个变量,不同于常量,变量是会变化的,因此我们就需要找到这个变量的变化递推式。
以上构造可以发现,如果是变量,就和原来的f(x)一样,只不过这个变量的递推式更加隐蔽,但是变量已经告诉我们了,我们就可以求出变量的递推式。
练习四
有以下递推式
已知a, b 求 f(a) + f(a + 1) + … + f(b - 1) + f(b)的值。
构造四
这下高级了,想让我们求区间和,但是对于区间和来说,我们应当立马联想到前缀和,两个前缀和就可以得到区间和。
好了,这下前缀和成为了我们的目的值,此时不妨引入T(n)代表前0~n的f值的前缀和。
很巧妙的构造,我们引入了前缀和,并且很好的使用了前缀和的构造式
练习五
f(0) = f(1) = 1
f(n) = X * f(n - 1) + Y * f(n - 2)
S(n) 为 A(i)^2的和 i从0~n
构造五
S(n)更加抽象,作为新引入的值,也是我们的目的值,它使用了平方,但实际上我们只要继续抓住规律,写出S(n)的构造式即可轻松解决
就有以下构造
此时我们甚至没有f(n)项,因为我们的目的是S(n),所以f(n)就是一个工具,为了便于使用,应该更加灵活。
练习六
一个01循环串,长度为L(L<=100),这个串每秒都会进行一次变换,变换规则是:如果左边是1,则改变自己的状态,否则保持不变。给定初始状态,问n秒以后这个串的状态。
构造六
题目已经是具体化的题目了,先进行抽象化,我们可以得到
建立的构造如下
构造是一件很灵活的事情,不能只拘泥于简单的递推上,要自己构建自己需要的值的构造式,才能得到构造矩阵。