[模板][题解][Luogu1939]矩阵乘法加速递推(详解)
题目大意:计算数列a的第n项,其中:
\]
\]
\]
一般的递推是O(n)的,显然时间和空间都不能承受。
由于每一步递推都是相同的。这句话包含了2个层面:首先,递推式是相同的;其次,递推的条件也要是相同的。综合来说,每一步的递推都是相同的。这是应用矩阵加速递推的充分条件。
那么怎么进行矩阵加速呢?我们首先观察,第\(i\)项和哪些项有关? 与\(i-3\)和\(i-1\)优化,也就是和前3项有关。为了能够**仅仅利用矩阵就能推出\(a[i]\), 我们需要记录前3项,于是,我们构造一个3*3的矩阵:
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}
\]
有同学会问:这个矩阵是怎么构造出来的呢?
我们首先要构造出类似于DP的状态来存下所有计算过程中可能会用到的信息,对于这道题,我们需要记录:(假设我们要从\(a[i]\)推到\(a[i+1]\))
a[i] \\
a[i-1] \\
a[i-2] \\
\end{bmatrix}
\]
这个矩阵要推到:
a[i+1] \\
a[i] \\
a[i-1] \\
\end{bmatrix}
\]
也就是说,我们需要构造一个矩阵\(A\),使得\(A*B = C\),根据线性代数的相关定义,A一定是一个\(3*3\)的矩阵,没错吧。
那好,我们已经得到:
? & ? & ? \\
? & ? & ? \\
? & ? & ? \\
\end{bmatrix}*\begin{bmatrix}
a[i] \\
a[i-1] \\
a[i-2] \\
\end{bmatrix}
=\begin{bmatrix}
a[i+1] \\
a[i] \\
a[i-1] \\
\end{bmatrix}
\\A*B=C
\]
我们只需要根据递推式,把矩阵\(A\)中的数填满就可以了,比如说:
由于$a[i+1] = a[i-2] +a[i] \(,而根据矩阵,\)a[i+1] = (0,0) * a[i] + (0,1) * a[i-1] + (0,2) * a[i-2]$,所以,矩阵的第一行是\(1,0,1\),以此类推,就可以吧矩阵填满了。
然后,我们可以得到:
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*\begin{bmatrix}
a[i] \\
a[i-1] \\
a[i-2] \\
\end{bmatrix}
=\begin{bmatrix}
a[i+1] \\
a[i] \\
a[i-1] \\
\end{bmatrix}
\\A*B=C
\]
可是,有了这个,怎么从\(a[1]\)推到\(a[n]\)呢?
我们知道:\(a[1] = a[2] = a[3] = 1\),如果把它们代入矩阵\(B\)(就是中间的那个矩阵),我们会得到:
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*\begin{bmatrix}
a[3]=1 \\
a[2]=1 \\
a[1]=1 \\
\end{bmatrix}
=\begin{bmatrix}
a[4]=1*a[3] + 1 * a[1] = 2 \\
a[3] = 1 \\
a[2] = 1 \\
\end{bmatrix}
\\A * B = C
\]
一开始我们只知道\(a[1], a[2], a[3]\),但是两个矩阵相乘后,我们得到了一个新的值\(a[4] = 2\),很开心有木有。如果我们用矩阵\(A\)去乘矩阵\(C\),会得到一个新的矩阵,暂且叫\(C'\),你会得到有一个新的值\(a[5]\),我们有点兴奋起来,这有点想多米诺骨牌,推到第一个,会一直向前倒,知道最后一个。我相信你脑子一定有了这样一个式子:
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*\begin{bmatrix}
a[3]=1 \\
a[2]=1 \\
a[1]=1 \\
\end{bmatrix}
=\begin{bmatrix}
a[n] \\
a[n-1] \\
a[n-2] \\
\end{bmatrix}
\]
矩阵乘法有结合律(但没有交换律,不要问我为什么),所以左边一堆相同的矩阵(不妨叫系数矩阵)可以用一个括号括起来,就像这样:
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}*
\begin{bmatrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}\right)*\begin{bmatrix}
a[3]=1 \\
a[2]=1 \\
a[1]=1 \\
\end{bmatrix}
=\begin{bmatrix}
a[n] \\
a[n-1] \\
a[n-2] \\
\end{bmatrix}
\]
想到了什么?
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0 \\
\end{bmatrix}^k*\begin{bmatrix}
a[3]=1 \\
a[2]=1 \\
a[1]=1 \\
\end{bmatrix}
=\begin{bmatrix}
a[n] \\
a[n-1] \\
a[n-2] \\
\end{bmatrix}
\]
我们可以得到\(k = n - 3\)(想想为什么?),由于n很大,能不能快速求矩阵k次方呢?
在mod p意义下?矩阵乘法满足结合律?
快速幂!
为什么可以用快速幂这个黑科技呢?
普通的快速幂是用来求\(b^k mod\ p\)的,其原理是把\(k\)二进制拆分成\(k=2^{a_1}+2^{a_2}+ ……+2^{a_k}\),从而得到\(b^k mod \ p = b^{2^{a_1}} * b^{2^{a_2}} * ……*b^{2^{a_k}} mod \ p = ((b^{2^{a_1}} mod \ p) *(b^{2^{a_2}} mod \ p) * …… * (b^{2^{a_k}} mod \ p))\ 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;
- C++数组从0开始的哦QAQ
- 计算答案时注意要加3项,不要只加2项
完整代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using 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;
}
[模板][题解][Luogu1939]矩阵乘法加速递推(详解)的更多相关文章
- HDU 5863 cjj's string game (矩阵乘法优化递推)
题目大意:用k种字符构建两个长度为n的字符串(每种字符有无限多个),要求对应位置字符相同的连续子串最长长度为m,问方法数. 其中k,n,m是输入,n(1<=n<=1000000000), ...
- 4-2.矩阵乘法的Strassen算法详解
题目描述 请编程实现矩阵乘法,并考虑当矩阵规模较大时的优化方法. 思路分析 根据wikipedia上的介绍:两个矩阵的乘法仅当第一个矩阵B的列数和另一个矩阵A的行数相等时才能定义.如A是m×n矩阵和B ...
- BZOJ 4870: [Shoi2017]组合数问题 矩阵乘法_递推
Code: #include <cstdio> #include <cstring> #include <algorithm> #define setIO(s) f ...
- 母牛的故事(hdoj 2018,动态规划递推,详解)
有一头母牛,它每年年初生一头小母牛.每头小母牛从第四个年头开始,每年年初也生一头小母牛.请编程实现在第n年的时候,共有多少头母牛? Sample Input2450Sample Output246 / ...
- POJ3070 Fibonacci(矩阵快速幂加速递推)【模板题】
题目链接:传送门 题目大意: 求斐波那契数列第n项F(n). (F(0) = 0, F(1) = 1, 0 ≤ n ≤ 109) 思路: 用矩阵乘法加速递推. 算法竞赛进阶指南的模板: #includ ...
- luogu题解 P1707 【刷题比赛】矩阵加速递推
题目链接: https://www.luogu.org/problemnew/show/P1707 分析: 洛谷的一道原创题,对于练习矩阵加速递推非常不错. 首先我们看一下递推式: \(a[k+2]= ...
- HDU 5950 - Recursive sequence - [矩阵快速幂加速递推][2016ACM/ICPC亚洲区沈阳站 Problem C]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5950 Farmer John likes to play mathematics games with ...
- CH 3401 - 石头游戏 - [矩阵快速幂加速递推]
题目链接:传送门 描述石头游戏在一个 $n$ 行 $m$ 列 ($1 \le n,m \le 8$) 的网格上进行,每个格子对应一种操作序列,操作序列至多有 $10$ 种,分别用 $0 \sim 9$ ...
- bzoj2004公交线路——DP+矩阵加速递推
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2004 求方案数,想到DP: 因为两个站间距离<=p,所以每p个站中所有车一定都会停靠至 ...
随机推荐
- 从浅入深详解独立ip网站域名恶意解析的解决方案
立IP空间的好处想必大家都能耳熟闻详,稳定性强,利于seo等让大家选择了鼎峰网络香港独立IP空间.那么, 网站独享服务器IP地址,独立IP空间利于百度收录和权重的积累.不受牵连.稳定性强等诸多优势为一 ...
- web服务器的原理
一:什么是web服务器 web服务器是可以向浏览器等Web客户端提供文档的计算机的程序.当web浏览器连接到服务器上并请求文件时,服务器将处理该请求并将文件发送到该浏览器上,附带的信息会告诉浏览器如何 ...
- 深入浅出mybatis之映射器
目录 概述 XML映射器 定义xml映射器 配置xml映射器 使用xml映射器 接口映射器 定义接口映射器 配置接口映射器 使用接口映射器 总结与对比 概述 映射器是MyBatis中最核心的组件之一, ...
- 练习:javascript轮播图效果
javascript轮播自动播放切换滑过停止,上一页/下一页 <!DOCTYPE html> <html lang="en"> <head> & ...
- ERROR [main] master.HMasterCommandLine Master exiting
2018-05-18 07:07:26,257 INFO [main-SendThread(localhost:2181)] zookeeper.ClientCnxn: Opening socket ...
- react简书
开发项目之前的准备 https://www.chromefor.com/ 登录此网站 下载相关crx react插件 不然要FQ 下载 React Developer Tools 谷歌插件下载 Re ...
- POJ 1269 Intersecing Lines (直线相交)
题目: Description We all know that a pair of distinct points on a plane defines a line and that a pair ...
- 【转】Java中文乱码的解决
在基于Java的编程中,经常会碰到汉字的处里及显示的问题,比如一大堆乱码或问号. 这是因为JAVA中默认的编码方式是UNICODE,而中国人通常使用的文件和DB都是基于GB2312或者BIG5等编码, ...
- 【原创】大叔案例分享(3)用户行为分析--见证scala的强大
一 场景分析 用户行为分析应用的场景很多,像线上网站访问统计,线下客流分析(比如图像人脸识别.wifi探针等),比较核心的指标有几个: PV | UV | SD | SC 指标说明: PV(Page ...
- 调用脚本的方式自动的创建或者是更新oracle数据库自带的Seq序列号的值
执行脚本: CREATE OR REPLACE PACKAGE PKG_QUERY IS -- Author : ADMINISTRATOR -- Created : 2016/12/8 星期四 10 ...