矩阵快速幂

by lcx,zjy

基础知识

矩阵:由$ mtimes n$个数排成的m行n列的数表
其实就是二维数组

矩阵加减法

矩阵加减法的规则:(Apm B=C)

其中$ C[i][j]$ 为(A[i][j])(B[i][j])的和或差,即:$ C_{i j}=A_{ij}pm B_{ij}$

因此,相加减的两个矩阵$ A :B$的行列必须相同

矩阵乘法

矩阵乘法的规则:(Atimes B=C)

其中$ C[i][j]$ 为A的第i行与B的第j列对应乘积的和,即:$ C_{i j}=displaystyle sum^n_{k=1}a_{ik}* b_{kj}$

显然两个相乘是要一行和一列对应乘,那么矩阵乘法是需要A的行数B的列数相等的,这是A*B的前提条件

这里给个例子帮助理解:

(left[ begin{matrix} a &b \c &d\e&fend{matrix} right]* left[ begin{matrix} g &h&i \j &k&lend{matrix} right]=left[ begin{matrix} ag+bj &ah+bk&ai+bl \cg+dj &ch+dk&ci+dl\eg+fj&eh+fk&ei+flend{matrix} right])

交换即是

$ left[ begin{matrix} g &h&i j &k&lend{matrix} right]*left[ begin{matrix} a &b c &de&fend{matrix} right]=left[ begin{matrix} ag+ch+ei &bg+dh+fiaj+ck+el&bj+dk+flend{matrix} right]$

可见矩阵的乘法是不满足交换律

然后就可以发现,矩阵(C)的行数应该是(A)的行数,列数应该是(B)的列数,并且(C)也是一个方阵(行数和列数相等的矩阵)

代码

int c[N][N];
void Mul(int a[][N],int b[][N],int n){//n是矩阵大小
    memset(c,0,sizeof(c));
    for(int i=1;i

应用

矩阵快速幂加速递推

矩阵快速幂

矩阵幂就是算$ A^n $

根据矩阵乘法,可以发现矩阵乘法满足结合律:

证明:

上面两式子都等于

于是——

假设A是$ n*n$的矩阵,则有:

$ A = begin{cases} A, & 当m = 1时 (A^{frac m2})^2, & 当m为偶数时 (A^{frac m2})^2times A, & 当m为奇数时 end{cases}$

这个分段函数说明了矩阵快速幂的可行性,然后我们就可以得出算法:

把快速幂算法中的乘法改成矩阵的乘法就可以了

不过呢,还有一个问题,ans一开始的初始化是什么?

ans的初始化就相当于普通快速幂需要初始化为1,即乘上这个矩阵值不改变

可以发现:对于任意(2 times 2)的矩阵,乘矩阵$ left[ begin{matrix} 1 &0 & 1end{matrix} right] $值不变,因此可以设其为初始矩阵

由此可推,ans的初始化就是对角线是1其他全是0

struct node{
    int z[N][N];
};
node mul(node a,node b){//矩阵乘法 
    node ans;
    memset(ans.z,0,sizeof(ans.z));
    for(int i=0;i>=1;//幂次/2
    }
    return ans;
}

ps:时间复杂度$ O(n^3logm)$

那么我们应该怎么加速递推呢?

先看一个简单的例子:

[POJ3070]Fibonacci

在Fibonacci整数序列中,(F_0) = 0, (F_1)=1,和(F_n) = (F_{n−1}) + (F_{n−2})(n≥2).给定整数n((0≤n≤10^9)),计算(F_n).

1.列解析式:显然:$ F(n)=F(n-1)+F(n-2)$,但这个数据范围就不是很显然了。

2.建立矩阵递推式,找到转移矩阵:

分析题目可以知道:

$F[i]=1*F[i-1]+1 *F[i-2] $

(F[i-1]=1*F[i-1]+0 *F[i-2])

将以上两个式子结合可得:

(left[ begin{matrix} F_{i-1} &F_{i-2} end{matrix} right]*left[ begin{matrix} 1 &1 \1 &0 end{matrix} right]=left[ begin{matrix} F_i &F_{i-1}end{matrix} right])

简写成$ F(n-1)*A=F(n)$,A矩阵就是那个2*2的常数矩阵

我们还可以把上述式子转换一下:

(( F[i] , F[i-1] )=( F[i-1] ,F[i-2] )*A=( F[i-2] ,F[i-3] ) *A *A)

最后可以得到:((F[n] F[n-1])=(F[1] ,F[0] )*A^{n-1}),即:(F(n)=F(1) *A^{n-1})

就愉快的转换成算矩阵快速幂了

于是——

考虑情况:$ F$是(1*n)的矩阵,$ A(是)nn(的矩阵,则)F'= FA$也是(1*n)的矩阵

(F)(F')可以看作是一维数组,省略他们的行下标1,按照矩阵乘法的定义,有:

$ F'j=displaystylesum{k=1}^nF_k*A_{kj}$

可以认为,通过乘上矩阵(A),从原始状态(F)递推到了(F')状态:

(left[ begin{matrix} F_1 &F_2 &F_3 end{matrix} right]times left[ begin{matrix} A_{11} &A_{12} &A_{13} \A_{21} &A_{22} &A_{23} \A_{31} &A_{32} &A_{33}end{matrix} right]=left[ begin{matrix} F'_1 &F'_2 &F'_3end{matrix} right])
那么如果假设目标状态为(G),递推矩阵为(A),初始条件为(F),则可得出:

(G=A^n*F)

因为我们已经会了矩阵快速幂算法,所以唯一需要我们考虑的问题就是如何构造递推矩阵(A)

再看几道题目:

Fibonacci前n项和

Fibonacci数列,f[1]=1,f[2]=1,f[n]=f[n-1]+f[n-2],((n>2)),输入n和m,求前n项和模m的值。((1leq nleq 2times 10^{9}),(1leq mleq 1times 10^9+10))

设$ s[n](表示前) n $项和,可推出:

$s[n]=1 * s[n-1]+1* f[n]+0 f[n-1]f[n+1]=0 s[n-1]+1f[n]+1 f[n-1]f[n]=0 s[n-1]+1 f[n]+0* f[n-1] $

因此,可得矩阵:

$[ s[n] f[n+1] f[n] ]=[s[n-1] f[n] f[n-1] ]*left[ begin{matrix} 1 & 0 & 0 1 & 1 & 1 &1 & 0 end{matrix} right] $

剩下的就和上一题一样了

[POJ3734]方块涂色

N个方块排成一列 用红,蓝,绿,黄4种颜色去涂色,求红色方块 和绿色方块个数同时为偶数的 方案数 对10007取余

1.列解析式

先定义状态分析递推式:假设已涂完前i个方块,有:
$ a[i](表示从1~i的方块中,红、绿方块数量都是偶数的方案数
)
b[i](表示从1~i的方块中,红、绿方块数量一个是偶数一个是奇数的方案数
)
c[i]$表示从1~i的方块中,红、绿方块数量都是奇数的方案数
初始:a(0)=1; b(0)=0; c(0)=0

分析a数组递推过程:

1.到i时红和绿的方格个数都是偶数,且i+1个方块被染成了蓝或黄色

2.到i时红和绿的方格个数一偶一奇,

且i+1个方块被染成了奇数个所对应的颜色

可得:(a[i+1]=2*a[i]+b[i])

b与c的分析如上,可得:

(b[i+1]=2*a[i]+2*b[i]+2*c[i])
(c[i+1]=b[i]+2*c[i])

2.建立矩阵递推式,找到转移矩阵:

由上可得:

$left[ begin{matrix} a_i&b_i&c_iend{matrix} right] *left[ begin{matrix} 2&2&0 1&2&1 &2& 2 end{matrix} right]=left[ begin{matrix} a_{i+1}&b_{i+1}&c_{i+1}end{matrix} right] $

矩阵快速幂加速递推题目特点

1.可以抽象为长度为n的一维数组(即状态矩阵),矩阵在单位时间内变化一次

2.变化的形式是线性递推(只有若干”加法“或“乘以一个系数”的运算)

3.递推轮数大,但矩阵长度n不大

构建矩阵递推的大致套路

上文常数矩阵$ A (就叫做**转移矩阵**,它能把) F[n-1] (转移到) F[n] (;然后这就是个等比数列,直接写出通项) F[n]=A^{n-1}*F[1](此处) f[1] $叫初始矩阵。

关键在于定义出状态矩阵和转移矩阵。

一般$ F[n](与)F[n-1] (都是按照原始递推式来构建的,当然可以先猜一个) F[n] $。

复杂度$ O(n^3logT)(,)T $是递归总轮数


矩阵表示修改

[THUSCH2017] 大魔法师

题目大意:n颗球,一颗球里有三个数(A) (B) (C)。有m次操作,每次操作选择一个区间([l,r])进行一下七种操作之一:

1.(A_i=A_i+B_i)

2.(B_i=B_i+C_i)

3.(C_i=C_i+A_i)

4.(A_i=A_i+v)

5.(B_i=B_itimes v)

6.(C_i=v)

7.输出(displaystylesum_{i=l}^rA_i),(displaystylesum_{i=l}^rB_i),(displaystylesum_{i=l}^rC_i)

对于区间修改,我们第一想法是线段树。但是每次修改都与该点中其他属性有关,故不能整体修改

于是就想矩阵乘法来改变状态:

把一颗球看作一个(1times 4)的矩阵([A,B,C,1])(最后一个(1)用来维护常项)

于是我们可以很轻易的推出转移矩阵:

1.([A,B,C,1]times begin{bmatrix} 1 & 0&0&0 \ 1 & 1&0&0\0&0&1&0\0&0&0&1 end{bmatrix}=[A+B,B,C,1])

2,3同理可得

4.([A,B,C,1]times begin{bmatrix} 1 & 0&0&0 \ 0 & 1&0&0\0&0&1&0\v&0&0&1 end{bmatrix}=[A+v,B,C,1])

5.([A,B,C,1]times begin{bmatrix} 1 & 0&0&0 \ 0 & v&0&0\0&0&1&0\0&0&0&1 end{bmatrix}=[A,B*v,C,1])

6.([A,B,C,1]times begin{bmatrix} 1 & 0&0&0 \ 0 & 1&0&0\0&0&0&0\0&0&v&1 end{bmatrix}=[A,B,v,1])

以第一种操作为例子,如果要修改([l,r])中的数据,那就把这段区间全部都乘一个(begin{bmatrix} 1 & 0&0&0 \ 1 & 1&0&0\0&0&1&0\0&0&0&1 end{bmatrix})就好了,于是就可以用线段树来维护了

[BZOJ2973]石头游戏

大意:有一个(n)(m)((0leq n,mleq 8))的矩阵,还有一个与之对应的(n)(m)列操作序列,一共有(act)种操作序列,编号(0到(act-1))((0leq actleq10))每一种操作序列都是长度不超过6,循环执行,一秒一个,所有格子同时进行包括:

数字0-9:拿0-9个石头到该格子

NWSE:把这个格子内所有的石头推到相邻的格子,N表示上方,W表示左方,S表示下方,E表示右方

D:拿走这个格子的石头。

问t秒((1leq tleq10^9))之后,所有方格中石头最多的格子有多少个石头

问题分析:

以样例为例,设定一维矩阵(F_t=[a_1 a_2 a_3 a_4 a_5 a_6])表示(t)秒时当前每个格子的石子数量,特别的,再加一个(a_0),使得(a_0)始终为(1),所以,转移矩阵(T_i)(0)列有且只有第(0)行为(1)

初始状态矩阵就是(F_0=[a_0=1 a_1=0 a_2=0 a_3=0 a_4=0 a_5=0 a_6=0])

第一秒的操作为(1,E,E,E,E,0),第1个格子+1,第2,3,4,5个格子推向右方,第6个格子不移动不添加

所以可以构造出转移矩阵(T_1=begin{bmatrix} 1 & 1&0&0&0&0&0 \ 0 & 1&0&0&0&0&0\0&0&0&1&0&0&0\0&0&0&0&1&0&0\0&0&0&0&0&1&0\0&0&0&0&0&0&1 \0&0&0&0&0&0&1 end{bmatrix})

因此也可以通过相同的方法找到(T_2)(T_3)(T_4)(T_5)...

因为(n)(m)的数据范围较小,所以我们可以把(n)(m)列的网络转化为长度为(ntimes m)的一维矩阵

(F_t=[a_{(1,1)},a_{(1,2)}...a_{(1,m)},a_{(2,1)}...a_{(n,m)}]),其中(a_{(i,j)})在一维矩阵第((i-1)times m+j)个位置,令(S(i,j)=(i-1)times m+j),也再加一个$ a_0$,始终为(1)

因为每个操作序列的长度不超过(6),且(1-6)的最小公倍数为(60),所以每经过(60)秒,操作序列又会从最开始的字符开始,因此需要构造(60)((ntimes m+1)times (ntimes m+1))转移矩阵(T),包含第(0-(ntimes m))行和第(0-(ntimes m))

转移矩阵(T_i(1leq ileq 60))的构造方法:

回顾:状态矩阵(F_i)所有元素与转移矩阵(T_{i+1})(i)列所有元素分别相乘的和,得到状态矩阵(F_{i+1})(i)个元素的数值

注:以下操作均不计除了当前石子外,其他石子的操作对此石子的影响

若操作数字为(0-9),设数值为(x),所以(T_i)(S(i,j))(0)(x),第(S(i,j))(S(i,j))(1)

若为字符(N),则转移矩阵第(S(i,j))(S(i-1,j))(1),字符(W,S,E)类似

若为字符(D),则转移矩阵此列不做处理

为了保证(F_i(0))始终为(1),所有转移矩阵(T_i)(0)列有且只有第(0)行为(1)

所以需要将(T_1-T_{60})全部求解出来,令(TT=T_1times T_2times ...times T_{60})

(t)秒后:

状态矩阵(F_t=F_0*TT^{frac{t}{60}}*(T_1times T_2times ...times T_r),r=t%60)

其中(TT^{frac{t}{60}})可以用矩阵快速幂求解,最后求(F_t)(1-(ntimes m))列的最大值即可

又可以发现一个规律:

如果在应用矩阵乘法时,遇到常数项,经常需要在“状态矩阵”中添加一个额外的位置,始终储存常数(1),并乘上“转移矩阵”中适当的系数,累加到“状态矩阵”的其他位置


矩阵乘法与邻接矩阵

[TJOI2017]可乐

题目:

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的(1)号城市上。这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆。它每一秒都会随机触发一种行为。现在给加里敦星球城市图,在第(0)秒时可乐机器人在(1)号城市,问经过了(t)秒,可乐机器人的行为方案数是多少?

(N)表示城市个数,(M)表示道路个数。

保证两座城市之间只有一条路相连,且没有任何一条道路连接两个相同的城市。

$ 1 (1≤N≤30,0

分析:

先用邻接矩阵存图(两个点之间若有边则(A[u][v]=1))

如果我们没有在原地停留和自爆两个操作,那么就是问从起点出发,走t步的不同路径数

令该图的邻接矩阵是(G),那么我们考虑 (G^2) 是个什么东西

我们单独考虑某一行和某一列的相关运算:令其为 $ G_{a,i}$和 $ G_{i,b}$令 (G′) 为相乘得到的矩阵,那么会有

$ G'{a,b}=displaystyle sum^m{i=1} G_{a,i}*G_{i,b}$

容易发现,当且仅当 $ G{a,i}$ 和 (G{i,b}) 都不为零,即(i)点可连通 (a,b) 两点的时候上式的该项才为(1), 否则为(0)

那么所有的这些情况累加起来,就是从(a)(b)长度为(2)的路径条数(即方案数)

所以,(G^2)得到的矩阵其实表示了任意两点间长度为2的方案数

(也从(floyd)算法的角度考虑)那么不难发现(G^k)的第(i)行第(j)列的数字含义是从(i)(j)经过(k)步的路径方案总数

那么在原地停留和自爆怎么处理?

在原地停留很简单,我们只要认为每个点都有一个从自己到自己的自环即可。

那自爆呢?

我们可以将自爆这个状态也看成一个城市,就设它为编号(0)

我们在邻接矩阵上从每个点都向这个点连一条边,这个点除了自己外不连其他出边。

这样就满足了任何一个点随时可以自爆,且无法恢复到其他状态。

最后,统计答案(Ans=sumlimits_{i=0}^{n}A[1][i])

文章来源于互联网:矩阵快速幂

THE END
分享
二维码