线性代数笔记
-
区间线性基/时间戳线性基:能加入则加入,不能加才不加
-
LGV引理应用:直接要求带符号和(P7736 [NOI2021] 路径交点),只有一种不交路径(CF348D Turtles),检验是否存在非交路径(随机赋权做),复杂度为求行列式的复杂度相同
-
矩阵树定理:求图的生成树方案数,复杂度为求行列式的复杂度相同
高斯 - 约旦消元法
记一个线性方程为 \([a_1,a_2,\cdots,a_n,b]\)。
记线性方程组的系数矩阵为 \(\begin{bmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\vdots&\vdots&\ddots&\vdots\\a_{n1}&a_{n2}&\cdots&a_{nn}\end{bmatrix}\)。
记线性方程组的增广矩阵为 \(\left[\begin{array}{c|c}\begin{matrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\vdots&\vdots&\ddots&\vdots\\a_{n1}&a_{n2}&\cdots&a_{nn}\end{matrix}&\begin{matrix}b_1\\b_2\\\vdots\\b_n\end{matrix}\end{array}\right]\)。
- 要做的是:把系数方阵消成对角矩阵。
- 从小到大,对于每个第 \(i\) 列,找到一个行 \(x\)(是之前没用过的,用
book数组记录),其第 \(i\) 列的值不为 \(0\),把这一行和第 \(i\) 行交换。
如果没有找到则没有唯一解。
-
用这一行把所有 \(x \neq i\) 的行 \(x\) 的第 \(i\) 个变为 \(0\)。
- 即:把每一个 \(x \neq i\) 的行的每一个数 \(a_{xk}\) 对位减去 \(\Large\frac{a_{ii}}{a_{xi}} \cdot a_{ik}\)。
-
消完后,\(\Large x_i=\frac{a_{ii}}{b_i}\)。
如果有一行是 \(0=0\),则有无穷多组解;如果有一行是 \(0=非0\),则无解
代码实现
#include <bits/stdc++.h>
using namespace std;
#define N 55
#define EPS 1e-6
double a[N][N];//把b[]放在a[][n+1]的地方
bitset<N>book;
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++){
scanf("%lf",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
static int now;
static bool flag;
flag=false;
for(now=1;now<=n;now++){
if(!book[now]&&abs(a[now][i]-0.0)>EPS){
flag=true;
break;
}
}
if(!flag){
continue;
}
swap(a[i],a[now]);
book[i]=true;
static double ti;
for(int j=1;j<=n;j++){
if(j==i||abs(a[j][i]-0.0)<EPS)continue;
ti=a[j][i]/a[i][i];
for(int k=i;k<=n+1;k++){
a[j][k]-=ti*a[i][k];
}
}
}
for(int i=1;i<=n;i++){
if(abs(a[i][i]-0.0)<EPS&&abs(a[i][n+1]-0.0)>EPS){
printf("-1");
return 0;
}
}
for(int i=1;i<=n;i++){
if(abs(a[i][i]-0.0)<EPS&&abs(a[i][n+1]-0.0)<EPS){
printf("0");
return 0;
}
}
for(int i=1;i<=n;i++){
printf("x%d=%.5lf\n",i,a[i][n+1]/a[i][i]);
}
return 0;
}
矩阵求逆
- 对原矩阵 \(A\) 做高斯消元/高斯-约旦消元的同时也对一个单位矩阵 \(E\) 做相同的操作。
- 原因:高斯消元/高斯-约旦消元的过程相当于对原矩阵乘了一个 \(A^{-1}\),所以对单位矩阵的相同操作就把单位矩阵做成了 \(A^{-1}\)。
高斯消元法解决行列式求值
-
根据行列式的性质:
- 交换两行(或列),行列式的值变为相反数。
- 可以将行列式的一行(或列)的 \(k\) 倍(\(k\in\mathbb{R}\))对位加到另一行(或列)上,行列式的值不变。
-
由这两条性质,用高斯消元把这个行列式变为只在主对角线上有非 \(0\) 值,再由定义,它的值就是: \(\prod\limits_{i=1}^na_{ii}\)。
-
如果高斯消元时,有一列无法消除,即使后面的都可以消除,但是主对角线上一定用一个值为 \(0\),所以该行列式的值就是 \(0\)。
-
注意:模意义下求值并且模数不是质数,则不能直接求逆元做高斯消元。
-
可以:对两行的第 \(i\) 个值做辗转相除法(类似GCD):
\[\begin{align} a[j][i](b)&\mathop{\Rightarrow}\limits^{\Large放到} a[i][i](a)\\ a[i][i]\mod a[j][i]=a[i][i]-\lfloor\frac{a[i][i]}{a[j][i]}\rfloor\times a[j][i](a\mod b=a-\lfloor\frac{a}{b}\rfloor\times b)&\mathop{\Rightarrow}\limits^{\Large放到} a[j][i](b) \end{align} \]并且更新其他列 \(k\)(减去 \(\lfloor\frac{a[i][i]}{a[j][i]}\rfloor\times a[j][k]\)),这样一定可以让其中一行的第 \(i\) 个值变为 \(0\),即可。
-
注意:用高斯-约旦消元法会使得上面原本第 \(x\) 行第 \(x\) 列有值的地方和其他是 \(0\) 的行交换,从而使行列式的值为 \(0\),是错误的。
-
时间复杂度
不严谨证明:对于行 \(i\) 的消元,
如果现在它和另一个行 \(j\) 进行辗转相除法消元,记 \(x=a_{ii},y=a_{ji}\)。
-
如果 \(x\),\(y\) 是倍数关系,虽然没有减小 \(x\) 的大小,但是只需要两轮(一轮是指行 \(i\) 和行 \(j\) 相减一次)就结束了。
-
如果 \(y\geq x\),则下一次会交换 \(x\),\(y\)。
-
-
如果 \(\frac{x}{2}\geq y\),则 \(x\mod y<y\leq \frac{x}{2}\)。
-
如果 \(\frac{x}{2}<y\),则 \(x\mod y=x-y<\frac{x}{2}\)。
-
-
这样现在的 \(y'\leq \frac{x}{2}\),即使下次只是交换 \(x\),\(y\),也让 \(x\) 变为一半大小,于是行 \(i\) 和所有其他行消元的轮数加起来是\(O(\log_2a_{ii})\)的。
-
于是:最终复杂度是\(n\times O(n+\log_2a_{ii})\times O(n)=O(n^2(n+\log_2a_{ii}))\) 。
-
-
代码实现
#include <bits/stdc++.h>
using namespace std;
#define N 605
long long MOD,a[N][N];
bitset<N>book;
int n;
bool ischanged=false;//是否变号
int main(){
scanf("%d%lld",&n,&MOD);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&a[i][j]);
a[i][j]%=MOD;
}
}
for(int i=1;i<=n;i++){
static int now;
static bool flag;
flag=false;
for(now=1;now<=n;now++){
if(!book[now]&&a[now][i]!=0){
flag=true;
break;
}
}
if(!flag){
printf("0");
return 0;
}
swap(a[i],a[now]);if(i!=now)ischanged^=1;
book[i]=true;
static long long ti;
for(int j=i+1;j<=n;j++){
if(j==i)continue;
while(a[j][i]!=0){
ti=a[i][i]/a[j][i];
for(int k=i;k<=n;k++){
a[i][k]=(a[i][k]-ti*a[j][k]%MOD+MOD);
if(a[i][k]>=MOD)a[i][k]-=MOD;
}
swap(a[i],a[j]);ischanged^=1;
}
}
}
long long ans=1;
for(int i=1;i<=n;i++){
ans=ans*a[i][i]%MOD;
}
printf("%lld",ischanged?((MOD-1)*ans%MOD):ans);
return 0;
}
浙公网安备 33010602011771号