矩阵及其应用
Basic
什么是矩阵
这真的是一个很复杂的问题…简单的说,矩阵是一个排列呈矩形的数集,比如说:
(1324)
矩阵的基本运算
在OI中,我们常用的是矩阵乘法,因此对于矩阵的加法、转置和数乘这里不做讲解。
对于一个 M×N 的矩阵 A 和一个 N×P 的矩阵 B,设矩阵 C 为 A 和 B 的乘积。
则 C 的第 i 行 第 j 列上的元素可表示为:
Ci,j=k=1∑NAi,k×Bk,j
也就是说,C 矩阵中第 i 行第 j 列的数是由 A 矩阵第 i 行的数与 B 矩阵第 j 列的数分别相乘再相加得到的。
矩乘的性质
- 矩乘只对形如 M×N 和 N×P 的两个矩阵有意义,且其结果一定是一个 M×P 的矩阵。
- 矩乘满足结合律,读者可根据定义式自行证明。
- 矩乘不满足交换律,根据定义式,这是显然的。
矩乘的代码实现
for(int i=1; i<=n; i++) {for(int j=1; j<=k; j++) {for(int h=1;h<=m;h++){c[i][j]+=a[i][h]*b[h][j];}}
}
就这么简单。
单位矩阵
单位矩阵是满足除了第 i 行第 j 列的数是 1 外,其他的数都是 0 的矩阵。
根据矩乘定义式,极易发现单位矩阵乘任何矩阵都得所乘矩阵。
矩阵快速幂
由于矩乘满足结合律,因此我们可以用快速幂计算。
代码如下:
struct Martrix{int a[4][4];Martrix(){memset(a,0,sizeof(a));}Martrix operator *(const Martrix B){Martrix res;for(int i=1;i<=3;i++){for(int j=1;j<=3;j++){for(int k=1;k<=3;k++){res.a[i][j]=(res.a[i][j]+a[i][k]*B.a[k][j])%mod;}} }return res;}void build(){for(int i=1;i<=3;i++){a[i][i]=1;}}
};
Martrix qpow(Martrix A,int n){Martrix res;res.build();while(n){if(n&1){res=res*A;}A=A*A;n>>=1;}return res;
}
没什么可说的。
矩阵的应用
矩阵优化递推
例:斐波那契数列
Fn 表示斐波那契数列中的第 n 项,给定 n≤1018,求 Fnmod109+7 的值
考虑构造矩阵:
(FnFn+1)
注意到:
(FnFn+1)=(Fn−1Fn)(0111)
也就是说,我们可以维护矩阵 (F1F2) ,然后一直乘 (0111)即可。
代码:
const int mod=1e9+7;
struct Martrix{int a[3][3];Martrix(){a[1][1]=a[1][2]=a[2][1]=a[2][2]=0;}Martrix operator *(const Martrix& B){Martrix res;for(int i=1;i<=2;i++){for(int j=1;j<=2;j++){for(int k=1;k<=2;k++){res.a[i][j]=(res.a[i][j]+a[i][k]*B.a[k][j])%mod;}}}return res;}void build(){a[1][1]=a[2][2]=1;}
};
Martrix qpow(Martrix a,int n){Martrix res;res.build();while(n){if(n&1){res=res*a;}a=a*a;n>>=1;}return res;
}
void solve(){int n;cin>>n;if(n<=2){cout<<1<<endl;return;}Martrix F,A;F.a[1][1]=1,F.a[1][2]=1;A.a[1][1]=A.a[1][2]=A.a[2][1]=1;F=F*qpow(A,n-2);cout<<F.a[1][1]<<endl;
} //缩进挂了,懒得修了