矩阵部分
矩阵乘法
两个矩阵做乘法运算时需要保证 \(A\) 的列等于 \(B\) 的行。
换句话来说,一个 \(n\times m\) 的矩阵 \(A\) 与一个 \(m\times k\) 的矩阵 \(B\) 相乘得到大小为 \(n\times k\) 的矩阵。
定义:
矩阵乘法运算律
\[A\times B≠B\times A \]\[(A\times B) \times C=A\times (B \times C) \]\[A\times [B \ C]=[A\times B \ A\times C] \]
其中 \([A\ B]\) 表示将矩阵 \(A,B\) 合并。
单位矩阵
\(n\) 阶单位矩阵 \(I\) 定义为:
性质:\(A\times I=A\)
初等行变换
这里只讲初等行变换。
初等行变换有三种操作:
将矩阵 \(A\) 的两行交换
将矩阵 \(A\) 的一行乘上同一个数
将矩阵 \(A\) 的一行减去另一行
性质:对矩阵 \(A\) 进行若干次初等行变换相当于在矩阵 \(A\) 的左边乘上一个矩阵 \(B\)。
初等矩阵
初等矩阵定义为 \(n\) 阶单位矩阵经过若干次初等行变换能够形成的矩阵。
逆矩阵
若一个 \(n\) 阶矩阵 \(A\),存在一个 \(n\) 阶矩阵 \(B\) 使得 \(A\times B=I\),那么称 \(B\) 是 \(A\) 的逆矩阵。
不是所有矩阵都有逆矩阵,必须是初等矩阵,由定义可知。
矩阵求逆
对于矩阵 \(A\),我们对它进行若干次初等变换后得到 \(I\)。等价于在 \(A\) 左侧乘上一堆矩阵。
联立两个式子
我们可以将这一坨
想象成进行若干次初等行变换,那么第一个式子相当于告诉我们 \(A\) 经过若干次变换后得到 \(I\),\(I\) 经过相同的操作就能得到 \(A\) 的逆矩阵 \(A^{-1}\)!
于是我们可以将 \(A\) 和 \(I\) 放到一起,然后将 \(A\) 变成 \(I\),那么 \(I\) 就变成了 \(A^{-1}\)。
具体应用
例 \(1:\)
设 \(f_0=0,f_1=1,f_2=1\),然后有
请用矩阵加速计算。
注意到 \(f_n\) 与 \(f_{n-1},f_{n-2},f_{n-3}\) 有关,那么我们构造矩阵 \(\begin{bmatrix} f_{n-2} & f_{n-1} & f_{n}\end{bmatrix}\),希望乘上一个 \(3\times 3\) 的矩阵得到 \(\begin{bmatrix} f_{n-1} & f_{n} & f_{n+1}\end{bmatrix}\)。
由于 \(f_{n-1}=0f_{n-2}+f_{n-1}+0f_n\),所以第一列构造 \(0,1,0\)。
同理我们可以依次构造出 \(\begin{bmatrix} 0 & 0 & 5\\1 & 0 & 7\\0 & 1 & 3\end{bmatrix}\)。
初始矩阵为 \(\begin{bmatrix} 0,1,1 \end{bmatrix}\),乘上矩阵 \(\begin{bmatrix} 0 & 0 & 5\\1 & 0 & 7\\0 & 1 & 3\end{bmatrix}\) \(n-2\) 次可以得到 \(f_n\),用矩阵快速幂可以做到 \(\log\) 复杂度。
例 \(2:\)
设 \(f_0=0,f_1=1\),然后
构造矩阵 \(\begin{bmatrix} 1 & 0 & 1\\0 & 0 & 6\\0 & 1 & 9 \end{bmatrix}\),初始矩阵为 \(\begin{bmatrix} 5,0,1 \end{bmatrix}\) 即可。
例 \(3:\)
P5175 数列
一个数列 $a_n $,已知 \(a_1\) 及 \(a_2\) 两项。
数列 \(a_n\) 满足递推式 \(a_n=x \times a_{n-1}+ y \times a_{n-2}(n≥3).\)
求 \(\sum_{i=1}^na_i^2\)
由于答案可能过大,对 \(10^9+7\) 取模。
此题的难点在于 \(a_i\) 多了一个平方。
但是题目给了我们 \(a_i\) 的通式,所以我们考虑将 \(a_i\) 展开。
\(\begin{aligned} a_i^2 &= (x\times a_{i-1}+y\times a_{i-2})^2 \\ &=x^2\times a_{i-1}^2+y^2\times a_{i-2}^2+2\times x\times y\times a_{i-1}\times a_{i-2}\\ \end{aligned}\)
发现展开项包括 \(a_i^2,a_{i-1}^2,a_ia_{i-1}\),因此考虑构造矩阵 \(\begin{bmatrix} a_{i-1}^2,a_{i}^2,a_ia_{i-1} \end{bmatrix}\)
由于 \(a_{i+1}=x\times a_i+y\times a_{i-1}\)
所以 \(a_ia_{i+1}=a_i(x\times a_{i}+y\times a_{i-1})=x\times a_i^2+y\times a_{i-1}\times a_i\)
那么我们需要乘上矩阵 \(\begin{bmatrix} 0 & y^2 & 0\\ 1 & x^2 & x^2\\0 & 2xy & y \end{bmatrix}\) 来得到矩阵 \(\begin{bmatrix} a_{i}^2,a_{i+1}^2,a_{i+1}a_i \end{bmatrix}\)。
但是这样还不够,因为我们要求的是 \(\sum_{i=1}^{n}a_i^2\),所以考虑在矩阵前面加上一个 \(ans_i\) 表示 \(\sum_{j=1}^{i}a_j^2\)
综上,我们的初始矩阵为 \(\begin{bmatrix} ans_{i-1},a_{i-1}^2,a_i^2,a_ia_{i-1} \end{bmatrix}\),操作矩阵为 \(\begin{bmatrix} 1 & 0 & 0 & 0\\0 & 0 & y^2 & 0\\1 & 1 & x^2 & x^2\\0 & 0 & 2xy & y \end{bmatrix}\),然后就可以矩阵快速幂了。
例 \(4:\)
P5136 sequence
题目描述
Wolfycz非常喜欢研究数列,同时他也喜欢研究黄金分割率,有一天Wolfycz写下了一个数列,他令\(A_i=\lceil(\dfrac{\sqrt{5}+1}{2})^i\rceil\),但是Wolfycz并不知道\(A_n\)的值,所以希望你来帮帮他
考虑构造方程使得 \(\dfrac{\sqrt{5}+1}{2}\) 与 \(\dfrac{-\sqrt{5}+1}{2}\) 是这个方程的两个解。
方程构造为 \(x^2-x-1=0\),那么 \(x^2=x+1\),两边同时乘上 \(x^{n-2}\) 可以得到 \(x^n=x^{n-1}+x^{n-2}\),然后记 \(f(i)=(\dfrac{\sqrt{5}+1}{2})^i+(\dfrac{-\sqrt{5}+1}{2})^i\),这样做的意义是使得 \(f(i)\) 是整数。那么 \(f(i)=f(i-1)+f(i-2)\),其中 \(f(0)=2,f(1)=1\)。
我们可以通过矩阵加速快速计算。
构造矩阵 \(\begin{bmatrix} f_{i-1},f_i \end{bmatrix}\),将这个矩阵乘上 \(\begin{bmatrix} 0 & 1\\1 & 1 \end{bmatrix}\) 可以得到 \(\begin{bmatrix} f_{i},f_{i+1} \end{bmatrix}\)。
这样我们就可以快速计算 \(f_n\)。
但是我们要求 \(\lceil(\dfrac{\sqrt{5}+1}{2})^i\rceil\),注意到 \(-1<\dfrac{-\sqrt{5}+1}{2}<0\),那么当 \(i\bmod 2=0\) 时,\(0<(\dfrac{-\sqrt{5}+1}{2})^i<1\),此时我们的答案不变。同理,我们可以求出 \(i\bmod 2=1\) 时的答案为 \(f(n)-1\),这样就完成了这道题。
P3263 [JLOI2015] 有意义的字符串
这道题和 sequence 差不多,我们同样只需要构造方程,在构造函数,最后分情况讨论即可,因此不在赘述。
P4783 【模板】矩阵求逆
这是一道板子题,直接放代码吧。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 810;
const int mod = 1e9+7;
int n,a[N][N];
int qpow(int a,int p,int m){
int res=1;
while(p){
if(p&1) res=res*a%m;
a=a*a%m;
p>>=1;
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i){
a[i][i+n]=1;
for(int j=1;j<=n;++j){
cin>>a[i][j];
}
}
for(int i=1;i<=n;++i){
int m=i;
for(int j=1;j<=n;++j){
if(j<i&&a[j][j]) continue;
if(a[j][i]>a[m][i]) m=j;
}
if(!a[m][i]){
puts("No Solution");
return 0;
}
swap(a[i],a[m]);
int mul=qpow(a[i][i],mod-2,mod);
for(int j=1;j<=2*n;++j) a[i][j]=a[i][j]*mul%mod;
for(int j=1;j<=n;++j){
if(i==j) continue;
int d=a[j][i];
for(int k=i;k<=2*n;++k){
a[j][k]=((a[j][k]-a[i][k]*d%mod)%mod+mod)%mod;
}
}
}
for(int i=1;i<=n;++i){
for(int j=n+1;j<=2*n;++j){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
线性代数部分
求解线性方程组
现在有这样一个方程组
\(\begin{cases}
a_{1,1}x_1+a_{1,2}x_2+\dots +a_{1,n}x_n=b_1\\
a_{2,1}x_1+a_{2,2}x_2+\dots +a_{2,n}x_n=b_2\\
\dots\\
a_{n,1}x_1+a_{n,2}x_2+\dots +a_{n,n}x_n=b_n\\
\end{cases}\)
其中 \(a_i,b_i∈ \Z\),求出方程的实数解。
考虑增广矩阵。
\(X=\begin{pmatrix}
a_{1,1},a_{1,2}\dots a_{1,n},b_1\\
a_{2,1},a_{2,2}\dots a_{2,n},b_2\\
\dots\\
a_{n,1},a_{n,2}\dots a_{n,n},b_n
\end{pmatrix}\)
那么问题化为对 \(X\) 进行若干次初等行变换消元。
高斯-约旦消元
高斯消元太麻烦,这里只讲高斯-约旦消元。
具体步骤是先对于每一列,找到最大的那个数,然后用最大的那个数去消元,原因是方便判断是否全为 0 和精度问题的考虑。
直接放代码
for(int i=1;i<=n;++i){
int m=i;
for(int j=1;j<=n;++j){
if(j<i&&!eq(fabs(a[j][j]),0)) continue;
//这个判断的原因是如果上面的”钦定“项为0,那么这里的xi被约掉了,也就是说,这个x是可以有无穷多个的,那么我们也可以用这一行的钦定值去消元。
if(fabs(a[m][i])<fabs(a[j][i])) m=j;
}
swap(a[m],a[i]);
if(eq(fabs(a[i][i]),0)) continue;
//如果最大值都为0了,那么这一列都是0,那么这里的xi有无穷多种
double d=a[i][i];
for(int j=i;j<=n+1;++j) a[i][j]/=d;
//先将这一列除一个d,使得系数为1
for(int j=1;j<=n;++j){
if(i==j) continue;
d=a[j][i];
for(int k=i;k<=n+1;++k) a[j][k]-=d*a[i][k];//消元
}
}
然后是判断无解或者无穷多组解的方法
bool ok=0;
for(int i=1;i<=n;++i){
if(eq(fabs(a[i][i]),0)){//这个的系数为0
ok=1;
if(!eq(fabs(a[i][n+1]),0)){
//方程0x!=0
puts("-1");//注意无解>无穷多解>有唯一解
return 0;
}
}
}
if(ok){
puts("0");
return 0;
}
高斯消元求解异或方程组
现在有一个异或方程组
\(\begin{cases}
a_{1,1} x_1\oplus a_{1,2}x_2\oplus\dots \oplus a_{1,n}x_n=b_1\\
a_{2,1} x_1\oplus a_{2,2}x_2\oplus\dots \oplus a_{2,n}x_n=b_2\\
\dots\\
a_{n,1} x_1\oplus a_{n,2}x_2\oplus\dots \oplus a_{n,n}x_n=b_n\\
\end{cases}\)
其中 \(a_{i,j},x_k,b_k∈\{0,1\}\)。
阁下该如何应对呢?
方法其实很简单,与求解线性方程组一样。考虑到第 \(i\) 列时,找到一个 \(1\) 然后让这一行去消元。
举个例子。
\(\begin{cases}
x_1\oplus x_2=1\\
x_2\oplus x_3=1\\
x_1\oplus x_3=0\\
\end{cases}\)
考虑构造增广矩阵
\(\begin{pmatrix}
1,1,0,1\\
0,1,1,1\\
1,0,1,0\\
\end{pmatrix}\)
然后让第三行去异或上第一行得到
\(\begin{pmatrix}
1,1,0,1\\
0,1,1,1\\
0,1,1,1\\
\end{pmatrix}\)
再让第一行异或上第二行
\(\begin{pmatrix}
1,0,1,0\\
0,1,1,1\\
0,1,1,1\\
\end{pmatrix}\)
再让第三行异或上第二行
\(\begin{pmatrix}
1,0,1,0\\
0,1,1,1\\
0,0,0,0\\
\end{pmatrix}\)
停止操作。但是发现最后一行全部为 \(0\),说明方程多解。最后的这个矩阵表示的含义为
\(\begin{cases}
x_2\oplus x_3=1\\
x_1\oplus x_3=0\\
\end{cases}\)
也就是
\(\begin{cases}
x_1=1-t\\
x_2=t\\
x_3=1-t\\
t∈\{0,1\}
\end{cases}\)
下面放上代码
for(int i=1;i<=n;++i){
int m=i;
for(int j=1;j<=n;++j){
if(j<i&&matrix[j][j]) continue;
if(matrix[j][i]>matrix[m][i]) m=j;
}
if(!matrix[m][i]) continue;
swap(matrix[m],matrix[i]);
for(int j=1;j<=n;++j){
if(i==j) continue;
if(matrix[j][i]){
for(int k=i;k<=n+1;++k) matrix[j][k]^=matrix[i][k];
}
}
}
甚至比普通还简单。。。
浙公网安备 33010602011771号