数学·线性代数

矩阵

运算法则

加法

两个矩阵 \(A\)\(B\) 相加,要求它们的维度相同。结果矩阵 \(C=A+B\) 的每个元素是 \(A\)\(B\) 对应元素的和。$$C_{i,j}=A_{i,j}+B_{i,j}$$

数乘

矩阵 \(A\) 与标量 \(k\) 相乘,结果矩阵 \(B=kA\) 的每个元素是 \(A\) 的对应元素乘以 \(k\)。$$B_{i,j}=k\cdot A_{i,j}$$

乘法

矩阵 \(A\) 与矩阵 \(B\) 相乘,要求 \(A\) 的列数等于 \(B\) 的行数。结果矩阵 \(C=AB\) 的每个元素是 \(A\) 的行向量与 \(B\) 的列向量的点积。$$C_{i,j}=\sum_{k=1}^nA_{i,k}\cdot B_{k,j}$$
注意:矩阵乘法不满足交换律

矩阵快速幂

其实和普通快速幂几乎一样。
我们要使用单位矩阵 \(I=\begin{bmatrix} 1&0&\cdots&0\\ 0&1&\cdots&0\\ \vdots&\vdots&\ddots&\vdots\\ 0&0&\cdots&1 \end{bmatrix}\) 作为初始矩阵。因为它乘任何矩阵还得跟它乘的那个矩阵,类似于 \(1\) 在普通快速幂中的作用。

#include<cstdio>
#include<cstring>
#define mod 1000000007
#define int long long
int n,k;
struct matrix{
	int a[110][110];
	matrix(){
		memset(a,0,sizeof(a));
	}
	matrix operator*(const matrix &b)const{
		matrix res;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				for(int k=1;k<=n;k++)
					res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
		return res;
	}
}ans,base,a;
void init(){
	for(int i=1;i<=n;i++)
		base.a[i][i]=1;
	return;
}
matrix qpow(matrix a,long long b){
	matrix res=base;
	while(b){
		if(b&1)
			res=res*a;
		a=a*a;
		b>>=1;
	}
	return res;
}
signed main(){
	scanf("%lld%lld",&n,&k);
	init();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%lld",&a.a[i][j]);
	ans=qpow(a,k);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)
			printf("%lld ",ans.a[i][j]);
		printf("\n");
	}
	return 0;
}

矩阵加速线性递推

例如斐波那契数列,\(F_1=F_2=1\)\(F_i=F_{i-1}+F_{i-2}\),矩阵递推形式为

\[\begin{bmatrix}F_{n-1}&F_{n-2}\end{bmatrix} \begin{bmatrix}1&1\\ 1&0\end{bmatrix}= \begin{bmatrix}F_n&F_{n-1}\end{bmatrix}\]

\(F_n\) 就是 \(\begin{bmatrix}1&1\end{bmatrix} \begin{bmatrix}1&1\\ 1&0\end{bmatrix}^{n-2}\) 的第一行第一列的元素。用矩阵快速幂即可。

高斯消元

现有线性方程组

\[\begin{cases}a_{1,1}x_1+a_{1,2}x_2+\cdots+a_{1,n}x_n=b_1\\ a_{2,1}x_1+a_{2,2}x_2+\cdots+a_{2,n}x_n=b_2\\ \cdots\\ a_{n,1}x_1+a_{n,2}x_2+\cdots+a_{n,n}x_n=b_n \end{cases}\]

求解该方程组。
为了方便起见,我们用矩阵表示方程组:

\[\left[\begin{matrix}a_{1,1}&a_{1,2}&a_{1,3}&\cdots&a_{1,n}\\ a_{2,1}&a_{2,2}&a_{2,3}&\cdots&a_{2,n}\\ a_{3,1}&a_{3,2}&a_{3,3}&\cdots&a_{3,n}\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ a_{n,1}&a_{n,2}&a_{n,3}&\cdots&a_{n,n} \end{matrix}\middle| \begin{matrix} b_{1}\\ b_{2}\\ b_{3}\\ \vdots\\ b_{n} \end{matrix}\right]\]

首先消去除 \(1\) 式以外所有式子中的 \(x_1\),然后消去除 \(1\) 式和 \(2\) 式外所有式子中的 \(x_2\)……最后式子变为一个三角形的结构

\[\left[\begin{matrix}a_{1,1}'&a_{1,2}'&a_{1,3}'&\cdots&a_{1,n}'\\ 0&a_{2,2}'&a_{2,3}'&\cdots&a_{2,n}'\\ 0&0&a_{3,3}'&\cdots&a_{3,n}'\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 0&0&0&\cdots&a_{n,n}' \end{matrix}\middle| \begin{matrix} b_{1}'\\ b_{2}'\\ b_{3}'\\ \vdots\\ b_{n}' \end{matrix}\right]\]

之后再一点点代回求解,时间复杂度 \(O(n^3)\)
对于无解的判断:某一行前 \(n\) 个数均为 \(0\),最后的结果却不为 \(0\)
对于无数解的判断:某一行 \(n+1\) 个数均为 \(0\)
模板

for(int i=1;i<=n;i++){
	int r=i;
	for(int j=i+1;j<=n;j++)
		if(fabs(matrix[r][i])<fabs(matrix[j][i]))
			r=j;//寻找主元 
	if(fabs(matrix[r][i])<eps){
		printf("No Solution");//若主元为0,方程组无解或无穷多解 
		return 0;
	}
	if(i!=r)
		swap(matrix[i],matrix[r]);
	double div=matrix[i][i];
	for(int j=i;j<=n+1;j++)
		matrix[i][j]/=div;
	for(int j=i+1;j<=n;j++){
		div=matrix[j][i];
		for(int k=i;k<=n+1;k++)
			matrix[j][k]-=matrix[i][k]*div;
	}//消元
}
ans[n]=matrix[n][n+1];
for(int i=n-1;i;i--){
	ans[i]=matrix[i][n+1];
	for(int j=i+1;j<=n;j++)
		ans[i]-=matrix[i][j]*ans[j];
}//回代

加强版

for(int i=1;i<=n;i++){
	int pivot=rank1+1;//rank1为矩阵的秩 
	for(int j=rank1+1;j<=n;j++)
		if(fabs(matrix[pivot][i])<fabs(matrix[j][i]))
			pivot=j;
	if(fabs(matrix[pivot][i])<eps)
		continue;//全为零
	if(pivot!=rank1+1)
		swap(matrix[pivot],matrix[rank1+1]);
	double div=matrix[rank1+1][i];
	for(int j=i;j<=n+1;j++)
		matrix[rank1+1][j]/=div;
	for(int j=1;j<=n;j++){
		if(j==rank1+1||fabs(matrix[j][i])<eps)
			continue;
		div=matrix[j][i];
		for(int k=i;k<=n+1;k++)
			matrix[j][k]-=matrix[rank1+1][k]*div;
	}//消元
	rank1++;
}
for(int i=rank1+1;i<=n;i++)
	if(fabs(matrix[i][n+1])>eps){
		printf("-1");
		return 0;//无解
	}
if(rank1<n){
	printf("0");
	return 0;//无数解
}
for(int i=n;i;i--){
	ans[i]=matrix[i][n+1];
	for(int j=i+1;j<=n;j++)
		ans[i]-=matrix[i][j]*ans[j];
}//回代

线性基

引入:有 \(n\) 个数,选出其中任意一些数,求其异或和,一共有多少种可能的异或值。

性质

  1. 原序列里面的任意一个数都可以表示为线性基的一个子集的异或和。
  2. 线性基任意一个子集的异或和都不能等于 \(0\)
  3. 线性基里面的数的个数唯一,并且在保持性质 1 的前提下,数的个数是最少的。
struct LBase{
	long long d[61];
	LBase(){
		memset(d,0,sizeof(d));
	}
	bool insert(long long x){
		for(int i=60;i>=0;i--){
			if(x&1ll<<i){
				if(d[i])
					x^=d[i];
				else{
					d[i]=x;
					return 1;
				}
			}
		}
		return 0;
	}
	long long query_max(){
		long long x=0;
		for(int i=60;i>=0;i--)
			if(d[i]&&(x^d[i])>x)
				x^=d[i];
		return x;
	}
	void merge(const LBase &a){
		for(int i=60;i>=0;i--)
			if(a.d[i])
				insert(a.d[i]);
		return;
	}
};
posted @ 2025-03-06 21:01  headless_piston  阅读(18)  评论(0)    收藏  举报