DP优化:矩阵乘法

## ~~话说这是博主的第一篇博客。。。~~ 
### 咳咳咳,今天讲的是DP的一种优化策略——矩阵乘法
关于能用矩阵乘法优化的DP题目,有如下几个要求:

1. 转移式只有加法,清零,减法etc.,max和min运算不允许
2. 转移式中关于前几位dp结果得到的系数必须是常量
3. 转移次数一般超级多
4. 由于转移次数多,一般都要模一个int范围内的数

综上,举一个例子:

> $dp[ i ]=a×dp[ i-1 ]+b×dp[ i-2]+c×dp[ i-3 ]$

其中,a,b,c是常量,而在需要矩阵优化的DP中,往往 i 在2^128之类的,特别鬼畜的特别大的数;
因为矩阵乘法优化后求dp[ i ] 是在O log(i)的时间内完成的。
那么,关于矩阵乘法如何实现,它的原理又是啥呢?
矩阵乘法需要两个矩阵A与B,A是n×p,B是p×m的大小,如下图![此图取自百度百科](https://img-blog.csdnimg.cn/20190504223656773.jpg)
为了方便解释,我们举斐波那契的例子。
斐波那契的转移式是:dp[ i ]=dp[ i-1 ]+dp[ i-2 ]。
那么我们把(dp[ i ],dp[ i-1 ])看做一个1×2的矩阵A
而每次转移相当于把A乘以矩阵F:
|1 1|
|1 0|
得出的结果是:$(dp[ i ]+dp[ i-1],dp[ i ])$,也就是$(dp[ i+1 ],dp[ i ])$
那么每次进行一次矩阵乘法需要8次运算,而原先的状态转移只需要1次,这么看矩阵乘法不就一废柴算法吗。。
关键的是!矩阵乘法具有**结合律**, 嘿嘿嘿,那么我们就可以开始**快速幂**了!这样一下吧O(n)的朴素算法优化成了O(8×logn)的算法,在**n**炒鸡炒鸡变态大的时候我们就可以用这个优化了。
[斐波那契原题](https://www.luogu.org/problemnew/show/P1962)
代码:

```cpp
#include<bits/stdc++.h>
using namespace std;
long long n;
const int MOD=1e9+7;
void mul(int f[2],int a[2][2]){
int c[2];
memset(c,0,sizeof(c));
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c[j]=(c[j]+(long long)f[k]*a[k][j])%MOD;
memcpy(f,c,sizeof(c));
}
void mulself(int a[2][2]){
int c[2][2];
memset(c,0,sizeof(c));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c[i][j]=(c[i][j]+(long long)a[i][k]*a[k][j])%MOD;
memcpy(a,c,sizeof(c));
}
int main(){
scanf("%lld",&n);
int f[2]={0,1};
int a[2][2]={{0,1},{1,1}};
for(;n;n>>=1){
if(n&1) mul(f,a);
mulself(a); 
}printf("%d\n",f[0]);
return 0;
}
```
斐波那契是二阶的矩阵乘法,复杂度为$O$(2^3^×logm),(m是需要DP到的序列的大小)还有三阶的甚至n阶的矩阵乘法,那样的话复杂度是$O$(n^3^×logm),关于三阶的例题:[三阶例题](https://www.luogu.org/problemnew/show/P1939)
三阶的话其实就把矩阵开成3×1和3×3的就可以了。
标程:

```cpp
#include<bits/stdc++.h>
using namespace std;
int T;
int n;
const int mod=1e9+7;
void mul(int f[3],int a[3][3]){
int c[3];
memset(c,0,sizeof(c));
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
c[j]=(c[j]+(long long)f[k]*a[k][j])%mod;
memcpy(f,c,sizeof(c));

void mulself(int a[3][3]){
int c[3][3];
memset(c,0,sizeof(c));
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
c[i][j]=(c[i][j]+(long long)a[i][k]*a[k][j])%mod;
memcpy(a,c,sizeof(c));
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int f[3]={0,0,1};
int a[3][3]={{1,1,0},{0,0,1},{1,0,0}};
for(;n;n>>=1){
if(n&1) mul(f,a);
mulself(a);
}
printf("%d\n",f[0]);
}
return 0;

```
呃,在这里打个**广告**:[博主自己出的题,n阶的,差不多紫题吧](https://www.luogu.org/problemnew/show/T77626)
~~讲真超级水~~ 
### 接下来是比较经典的例题:
##### **例一**
设一个函数为f(n),表示从1到n所有整数连起来的数,例如:f(1)=1,f(6)=123456,f(11)=1234567891011。求f(n)模1e9+7的值
范围超大:n<=1e18
emmm,看的这道题,你发现就算是不模1e9+7把1e18这么多数直接输出都会爆炸,于是这题的算法就只能是O(logn)得啦QAQ。
于是我们发现f(n)可以由f(n-1)后面接上n得到,那么得出一个不可能计算的3×3转移矩阵:
| 10^log(n-1)^ 0 0|
|1 1 0|
|0 1 1|
而被乘矩阵为|f(n),n+1,1|
很明显,10^log(n-1)^是不可能算出来的,那么我们可以这样做:
例如n=999
|0 1 1| ×|10 0 0|^9^×|100 0 0|^90^×|1000 0 0|^899^
________|1 1 0| ___ |1 1 0| ______|1 1 0|
________|0 1 1| ___ |0 1 1| ______|1 1 0|
就解决啦啦啦啦(~ ̄▽ ̄)~

原文地址:https://www.cnblogs.com/china-xyc/p/11616820.html

时间: 2024-10-07 19:11:52

DP优化:矩阵乘法的相关文章

【BZOJ2510】弱题 期望DP+循环矩阵乘法

[BZOJ2510]弱题 Description 有M个球,一开始每个球均有一个初始标号,标号范围为1-N且为整数,标号为i的球有ai个,并保证Σai = M. 每次操作等概率取出一个球(即取出每个球的概率均为1/M),若这个球标号为k(k < N),则将它重新标号为k + 1:若这个球标号为N,则将其重标号为1.(取出球后并不将其丢弃) 现在你需要求出,经过K次这样的操作后,每个标号的球的期望个数. Input 第1行包含三个正整数N,M,K,表示了标号与球的个数以及操作次数. 第2行包含N个

Strassen优化矩阵乘法(复杂度O(n^lg7))

按照算法导论写的 还没有测试复杂度到底怎么样 不过这个真的很卡内存,挖个坑,以后写空间优化 还有Matthew Anderson, Siddharth Barman写了一个关于矩阵乘法的论文 <The Coppersmith-Winograd Matrix Multiplication Algorithm> 提出了矩阵乘法的O(n^2.37)算法,有时间再膜吧orz #include <iostream> #include <cstring> #include <

poj3613:Cow Relays(倍增优化+矩阵乘法floyd+快速幂)

Cow Relays Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7825   Accepted: 3068 Description For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race using the T (2 ≤ T ≤ 100) cow trails throughout

【POJ2778】AC自动机,DP,矩阵乘法

题意:给出n个字串表示"缺陷基因",然后让求长度为m的基因(4^m个)中有多少个不带病. 题解:首先建立AC自动机,然后从每个节点开始选"ATGC"有四种往外转移的途径. 如:ACG,C这两个基因建一个ACauto,然后转移矩阵为下. 2 1 0 0 1 2 1 1 0 0 1 1 0 1 1 2 1 0 0 1 2 1 0 0 1 然后把危险状态删去(赋0),即基因结束节点的行和列. 然后矩阵变成 2 1 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0

利用Cayley-Hamilton theorem 优化矩阵线性递推

平时有关线性递推的题,很多都可以利用矩阵乘法来解k决. 时间复杂度一般是O(K3logn)因此对矩阵的规模限制比较大. 下面介绍一种利用利用Cayley-Hamilton theorem加速矩阵乘法的方法. Cayley-Hamilton theorem: 记矩阵A的特征多项式为f(x). 则有f(A)=0. 证明可以看 维基百科 https://en.wikipedia.org/wiki/Cayley–Hamilton_theorem#A_direct_algebraic_proof 另外我在

[BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j 位的字符串个数,然后转移就是可以从第 j 位加上一个字符转移到另一个位置. 然而..我并没有写过KMP + DP,我觉得还是写AC自动机+DP比较简单..于是,尽管只有一个模式串,我还是写了AC自动机+DP. 然后就是建出AC自动机,f[i][j] 表示长度为 i ,走到节点 j 的字符串的个数.

矩阵乘法优化dp

前几天学姐说要给我们考矩乘dp和期望dp...其实我们都(也可能只有我)不会. 那只能现学了,然而学了一天突然通知不考了qwq 矩阵乘法 A矩阵为m*k,B矩阵为k*n,两矩阵相乘则得C矩阵为m*n; for (int i=1;i<=M;++i) for (int j=1;j<=N;++j) for (int k=1;k<=K;++k) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod; 矩阵乘法模板  时间复杂度为$O(N^3)$,数学一本通上

形态形成场(矩阵乘法优化dp)

形态形成场(矩阵乘法优化dp) 短信中将会涉及前\(k\)种大写字母,每个大写字母都有一个对应的替换式\(Si\),替换式中只会出现大写字母和数字,比如\(A→BB,B→CC0,C→123\),代表 \(A=12312301231230,B=1231230,C=123\).现在对于给定的替换式,求字符 AA 所代表的串有多少子串满足: 这个子串为单个字符\(0\)或没有前导\(0\). 把这个子串看作一个十进制数后模\(n\)等于\(0\). 答案对\(r\)取模.对于100%的数据,$2 \l

BZOJ 1875 SDOI 2009 HH去散步 矩阵乘法优化DP

题目大意:给出一张无向图,求从A到B走k步(不能走回头路)的方案数.(k <= 2^30) 思路:看到k的范围就知道是矩阵乘法了.关键是不能走回头路怎么构造.正常的方法构造点的转移不能避免这个问题,就用边来构造.只要保证不经过自己^1的边就可以保证不走回头路了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX