线性代数笔记

  • 区间线性基/时间戳线性基:能加入则加入,不能加才不加

  • 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]\)

  • 要做的是:把系数方阵消成对角矩阵。
  1. 从小到大,对于每个第 \(i\) 列,找到一个行 \(x\)(是之前没用过的,用book数组记录),其第 \(i\) 列的值不为 \(0\),把这一行和第 \(i\) 行交换。
  • 如果没有找到则没有唯一解。
  1. 用这一行把所有 \(x \neq i\) 的行 \(x\) 的第 \(i\) 个变为 \(0\)

    • 即:把每一个 \(x \neq i\) 的行的每一个数 \(a_{xk}\) 对位减去 \(\Large\frac{a_{ii}}{a_{xi}} \cdot a_{ik}\)
  2. 消完后,\(\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}\)

高斯消元法解决行列式求值

  • 根据行列式的性质:

    1. 交换两行(或列),行列式的值变为相反数。
    2. 可以将行列式的一行(或列)的 \(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}\)

      1. 如果 \(x\)\(y\) 是倍数关系,虽然没有减小 \(x\) 的大小,但是只需要两轮(一轮是指行 \(i\) 和行 \(j\) 相减一次)就结束了。

      2. 如果 \(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}\)

      3. 这样现在的 \(y'\leq \frac{x}{2}\),即使下次只是交换 \(x\)\(y\),也让 \(x\) 变为一半大小,于是行 \(i\) 和所有其他行消元的轮数加起来是\(O(\log_2a_{ii})\)的。

      4. 于是:最终复杂度是\(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;
} 
posted @ 2025-07-16 20:44  mike_666  阅读(27)  评论(0)    收藏  举报