矩阵
本质是一个二维数表
A=a1,1a2,1⋮am,1a1,2a2,2⋮am,2……⋱…a1,na1,n⋮am,n
矩阵乘法
A 为 m×n 的矩阵,B 为 n×p 的矩阵,C=A×B,C 中第 i 行第 j 列元素可以表示为:
ci,j=k=1∑nai,k×bk,j
示例:
[132456]×791181012=[(1×7+2×9+5×11)(3×7+4×9+6×11)(1×8+2×10+5×12)(3×8+4×10+6×12)]=[8012388136]
换句话说,就是用矩阵 A 的第 i 行元素乘矩阵 B 的第 j 列元素求和。
广义矩阵乘法
ci,j=k=1minn(ai,k+bk,j)ci,j=k=1∑nmax(ai,k,bk,j)⋮
广义矩阵乘法可以是自行定义的运算,可以用来解决各种递推问题。但要保证满足结合律,以使用矩阵快速幂。
矩阵乘法的性质
事实上,矩阵乘法满足幺半群的性质。
-
矩阵乘法有封闭性:
显然,矩阵与矩阵相乘,得到的还是矩阵。
-
矩阵乘法有结合律:
D=A×B×C=A×(B×C)
尝试从代数角度进行证明
di,j=l=1∑Bcol(k=1∑Brow(ai,k×bk,l)×cl,j)=l=1∑Bcolk=1∑Browai,k×bk,l×cl,j=k=1∑Browai,k×(l=1∑Bcolbk,l×cl,j)
-
矩阵乘法存在幺元
定义单位矩阵
In=10⋮001⋮0……⋱…00⋮1
则有A×In=A。
矩阵快速幂
洛谷P3390 【模板】矩阵快速幂
由于矩阵乘法满足结合律,可以利用倍增的思想,在对数复杂度内解决矩阵幂运算的问题。代码复杂度 O(n3logk)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include<bits/stdc++.h> using namespace std; #define ll long long
const int N = 105; const ll MOD = 1e9+7;
struct Matrix{ int row,col; ll d[N][N]; Matrix(int r,int c){ row=r,col=c; memset(d,0,sizeof(d)); } };
Matrix time(Matrix a,Matrix b){ Matrix c(a.row,b.col); if(a.col != b.row)return c; for(int i=0;i<a.row;i++){ for(int j=0;j<b.col;j++){ for(int k=0;k<a.col;k++){ c.d[i][j] += a.d[i][k] * b.d[k][j]; c.d[i][j] %= MOD; } } } return c; }
Matrix pow(Matrix a,ll p){ Matrix res(a.col,a.col); for(int i=0;i<a.col;i++){ res.d[i][i]=1; }
while(p>0){ if(p&1)res=time(res,a); p>>=1; a=time(a,a); } return res; }
int main(){ ll n,k; scanf("%lld%lld",&n,&k); Matrix a(n,n); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ scanf("%d",&a.d[i][j]); } }
auto ans = pow(a,k); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ printf("%lld ",ans.d[i][j]); } puts(""); } return 0; }
|
矩阵快速幂加速递推
题意:求斐波那契数列第n项对109+7求余的结果,n<263。
显然如果递推的话,复杂度 O(n),肯定会超时。
这时,我们考虑用矩阵乘法加速。
由递推式Fn=Fn−1+Fn−2,我们可以列出如下等式:
[FnFn−1]=[Fn−1Fn−2]×[1110]
则
[FnFn−1]=[F2F1]×[1110]n−2
时间复杂度 O(logn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<bits/stdc++.h> using namespace std;
#define ll long long const ll MOD = 1e9+7;
struct Matrix{ int row,col; ll d[5][5]; Matrix(int r,int c){ row=r,col=c; memset(d,0,sizeof(d)); } }; Matrix time(Matrix a,Matrix b){ Matrix c(a.row,b.col); if(a.col != b.row)return c; for(int i=0;i<a.row;i++){ for(int j=0;j<b.col;j++){ for(int k=0;k<a.col;k++){ c.d[i][j] += a.d[i][k] * b.d[k][j]; c.d[i][j] %= MOD; } } } return c; } Matrix pow(Matrix a,ll p){ Matrix res(a.col,a.col); for(int i=0;i<a.col;i++){ res.d[i][i]=1; }
while(p>0){ if(p&1)res=time(res,a); p>>=1; a=time(a,a); } return res; }
int main(){ ll n; scanf("%lld",&n); if(n<=2){ puts("1"); return 0; } Matrix a(2,2); a.d[0][0]=a.d[0][1]=a.d[1][0]=1; Matrix b(1,2); b.d[0][0] = b.d[0][1] = 1; auto ans = time(b,pow(a,n-2)); printf("%lld\n",ans.d[0][0]); return 0; }
|
求斐波那契数列前 n 项和
此时,有两个递推式Fn=Fn−1+Fn−2,Sn=Sn−1+Fn。我们可以将两个递推式写在同一个等式中。
[FnFn−1Sn]=[Fn−1Fn−2Sn−1]×110100111
[HDU2157]How many ways??
题意:给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值。
有递推式
Fu,k=(v,u)∈E∑Fv,k−1
可以构建如下矩阵
[F1,kF2,k…Fn,k]=[F1,k−1F2,k−1…Fn,k−1]×a1,1a1,2⋮a1,na2,1a2,2⋮a2,n……⋱…an,1an,2⋮an,n
其中 ai,j 为从 i 到 j 的边的个数,在没有重边的情况下,这个矩阵正好等于这张图的邻接矩阵。
[POJ3613]恰好k步最短路
题意:给定一个有向带权图,问从A点恰好走k步(允许重复经过边)到达B点的最短路。
有递推式
Fu,k=(v,u)∈Emin(Fv,k−1+wu,v)
考虑定义矩阵乘法
ci,j=k=1minn(ai,k+bk,j)
则有
[F1,kF2,k…Fn,k]=[F1,k−1F2,k−1…Fn,k−1]×w1,1w1,2⋮w1,nw2,1w2,2⋮w2,n……⋱…wn,1wn,2⋮wn,n
问题解决。