线性代数初步

1

矩阵乘法规则:
\(\begin{bmatrix}a_{1,1}&a_{1,2}&a_{1,3}\\a_{2,1}&a_{2,2}&a_{2,3}\end{bmatrix} \times \begin{bmatrix}b_{1,1}&b_{1,2}\\b_{2,1}&b_{2,2}\\b_{3,1}&b_{3,2}\end{bmatrix}=\begin{bmatrix}a_{1,1}b_{1,1}+a_{1,2}b_{2,1}+a_{1,3}b_{3,1}&a_{1,1}b_{1,2}+a_{1,2}b_{2,2}+a_{1,3}b_{3,2}\\a_{2,1}b_{1,1}+a_{2,2}b_{2,1}+a_{2,3}b_{3,1}&a_{2,1}b_{1,2}+a_{2,2}b_{2,2}+a_{2,3}b_{3,2}\end{bmatrix}\).
以此类推,若 \(A \times B = C\) ,则 \(C\)\(m\) 行第 \(n\) 列的元素等于 \(A\)\(m\) 行的元素与 \(B\)\(n\) 列的元素对应乘积之和。

struct mat
{
	int r,c;
	int A[N][N];
	mat()
	{
		memset(A,0,sizeof(A));
	}
};
mat operator * (mat x,mat y)
{
	mat ans;
	ans.r=x.r,ans.c=y.c;
	for(int i=1;i<=ans.r;i++)
	 	for(int j=1;j<=ans.c;j++)
	 		for(int k=1;k<=x.c;k++)
	 			ans.A[i][j]+=x.A[i][k]*y.A[k][j]%p,ans.A[i][j]%=p;
	return ans;
}

若要算连续的矩阵乘法,可以用快速幂优化,写法与普通快速幂类似。
根据矩阵乘法的计算方式,它可以用来处理某些类型的 dp 。
尤其是当状态数较少但转移次数较多,且转移相同时,用矩阵快速幂优化可以达到很好的复杂度。

2

高斯消元

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define db double
using namespace std;
const int N=110;
const db eps=1e-9;
int n;
db a[N][N],ans[N];
db ABS(db x)
{
	return x>eps?x:-x;
}
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++)
	{
		int t=i;
		for(int j=i+1;j<=n;j++)
			if(ABS(a[j][i])-ABS(a[t][i])>eps) t=j;
		swap(a[t],a[i]);
		//找到第i个元素最大的行,换上来(减小精度误差) 
		if(ABS(a[i][i])<eps)
			puts("No Solution"),exit(0);
		for(int j=i+1;j<=n+1;j++) a[i][j]/=a[i][i];
		a[i][i]=1;
		//把系数消成1,方便后面的操作 
		for(int j=i+1;j<=n;j++)
		{
			for(int k=i+1;k<=n+1;k++)
				a[j][k]-=a[i][k]*a[j][i];
			a[j][i]=0;
		}
	}
	for(int i=n;i>=1;i--)
	{
		ans[i]=a[i][n+1];
		for(int j=i+1;j<=n;j++) ans[i]-=a[i][j]*ans[j];
	}
	for(int i=1;i<=n;i++) printf("%.2lf\n",ans[i]);
	return 0;
}

3

求行列式
与高斯消元类似,但是不能直接使用实数(避免误差)。
要用一行消去另一行时,用辗转相减的思想,每次消去最大的能消的整数倍,再把两行交换,以此类推。
注意,交换时行列式变号。
复杂度仍为 \(O(n^3)\)

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=1010;
int n,p;
int a[N][N];
signed main()
{
	scanf("%lld%lld",&n,&p);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%lld",&a[i][j]),a[i][j]%=p;
	int ans=1;
	for(int i=1;i<n;i++)
		for(int j=i+1;j<=n;j++)
		{
			while(a[i][i])
			{
				int t=a[j][i]/a[i][i];
				for(int k=i;k<=n;k++)
					a[j][k]-=a[i][k]*t%p,a[j][k]+=p,a[j][k]%=p;
				swap(a[i],a[j]),ans=-ans;
			}
			swap(a[i],a[j]),ans=-ans;
		}
	for(int i=1;i<=n;i++) ans*=a[i][i],ans%=p;
	printf("%lld",(ans+p)%p);
	return 0;
}

4

矩阵树定理。
1
无向无权图。
\(A\) 为邻接矩阵,\(D\) 为度数矩阵(\(D_{i,i}\)\(i\) 点的度数,其它值为 0)。
基尔霍夫矩阵为 \(K=A-D\)
\(K'\)\(K\) 去掉第 \(k\) 行第 \(k\) 列后得到的矩阵,
\(\det(K')\) 即为该图的生成树个数。
(取任意 \(k\) 时得到的结果均相等)
还可以处理有重边的情况。(若 \(i\)\(j\) 之间有 \(x\) 条边,则 \(A_{i,j}=A_{j,i}=x\)
2
有向图。
若根为 \(r\) ,则 \(K'\)\(K\) 去掉第 \(r\) 行第 \(r\) 列后得到的矩阵。
求内向生成树(由外向根)时, \(D_{i,i}=\sum \limits_{j=1}^n A_{i,j}\) .
求外向生成树(由根向外)时, \(D_{i,i}=\sum \limits_{j=1}^n A_{j,i}\) .
有重边时,同样可以处理。
3
有权图。
把边权理解为重边,同样用上面的方法。
此时求的就是所有生成树边权乘积的总和。

posted @ 2020-04-23 20:42  BBD186587  阅读(249)  评论(2编辑  收藏  举报