线性代数笔记1
一.行列式
1.行列式定义
\(
\left | \begin{matrix}
a_{11} &a_{12} &... &a_{1n} \\
a_{21} &a_{22} &... &a_{2n} \\
... &... &... &...\\
a_{n1} &a_{n2} &... &a_{nn}
\end{matrix} \right |
\)
将此称为\(n\)阶行列式
设\(p\)数组为\(n\)的全排列,\(t\)为\(p\)数组的逆序对数
则此行列式的值为\(\sum (-1)^ta_{1p_{1}}a_{1p_{2}}...a_{1p_{n}}\)
2.重要的结论及性质
1.上三角行列式的值为对角线的乘积
2.下三角行列式的值为对角线的乘积
3.对角行列式的值为对角线的乘积
4.若互换行列式的任意两行(列),则行列式变号
5.若行列式的一行乘上一个数k,则行列式的值也乘上一个数k
6.若行列式的一行同时加(减)一个数,该行列式可拆成两个行列式之和
3.在\(O(n^3+n^2logn)\)的时间复杂度内求行列式的值
行列式求值模版
本题坑点:p不为素数
此题可以通过高斯消元将原行列式变换成三角行列式(因时间复杂度不足(可能是我太菜了),无法变换成对角行列式),再求出对角乘积即可,但因为模数为素数,所以需要辗转相减,所以时间复杂度为\(O(n^3+n^2logn)\),代码如下:
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
int jz[605][605];
int n;
int p;
signed main(){
scanf("%lld%lld",&n,&p);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&jz[i][j]);
jz[i][j]%=p;
}
}
int w=1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(i==j){
continue;
}
/*int bs=jz[j][i]/jz[i][i];
for(int k=1;k<=n;k++){
jz[j][k]=(jz[j][k]-jz[i][k]*bs%p+p)%p;
}*/
/*if(jz[i][i]>jz[j][i]){
for(int k=1;k<=n;k++){
swap(jz[i][k],jz[j][k]);
}
w=-w;
}*/
while(jz[i][i]!=0){
int bs=jz[j][i]/jz[i][i];
for(int k=1;k<=n;k++){
jz[j][k]=(jz[j][k]-bs*jz[i][k]%p+p)%p;
}
for(int k=1;k<=n;k++){
swap(jz[j][k],jz[i][k]);
}
w=-w;
}
for(int k=1;k<=n;k++){
swap(jz[j][k],jz[i][k]);
}
w=-w;
}
}
/*for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",jz[i][j]);
}
printf("\n");
}*/
int ans=1;
for(int i=1;i<=n;i++){
ans*=jz[i][i];
ans%=p;
}
ans=(ans*w+p)%p;
printf("%lld",ans);
return 0;
}
4.matrix-tree定理
对于无向图的基尔霍夫矩阵定义:\(T_{i,j}=\begin{cases}\sum_{k=1}^{n}w_{i,k}
&(i==j) \\
-w_{i,j} &(i\ne j)\end{cases}\)
该矩阵有如下性质:
1.该矩阵的任意一行或任意一列的和是相等的(显然)
推论:该矩阵对应的行列式的值为0
2.若将该矩阵的同编号的一行和一列删除,则该行列式的绝对值为生成树的个数。
对于有向图的基尔霍夫矩阵(外向树)定义:\(T_{i,j}=\begin{cases}\sum_{k=1}^{n}w_{k,i}
&(i==j) \\
-w_{i,j} &(i\ne j)\end{cases}\)
此矩阵有一特殊性质:若将该矩阵的同编号的一行和一列删除,则该行列式的绝对值为以此编号为根的生成树(外向树)的个数。
内向树与外向树的区别:外向树ij时记录的是入度,而内向树ij时记录的是出度。
对于三种情况:
1.无向图生成树计数
使用无向图的基尔霍夫矩阵,删除任意一组同编号的一行和一列即可
2.有向图外向树计数
使用有向图的基尔霍夫矩阵(外向树),删除根节点编号的一行和一列即可
3.有向图内向树计数
使用有向图的基尔霍夫矩阵(内向树),删除根节点编号的一行和一列即可
加权扩展:
若图有边权,则根据乘法原理,可以将边权看作重编,所以注意度数数组
Matrix-tree模版
代码:
#include <cstdio>
#define int long long
#define mod 1000000007
using namespace std;
int n,m,t;
struct hls{
int k1;
int d[305][305];
/*friend hls operator - (hls a,hls b){
hls ans;
ans.k1=a.k1;
for(int i=1;i<=ans.k1;i++){
for(int j=1;j<=ans.k1;j++){
ans.d[i][j]=a.d[i][j]-b.d[i][j];
}
}
return ans;
}*/
int qz(){
int w=1;
for(int i=1;i<=k1;i++){
for(int j=i+1;j<=k1;j++){
if(d[j][i]>d[i][i]){
for(int k=1;k<=k1;k++){
int tmp;
tmp=d[j][k];
d[j][k]=d[i][k];
d[i][k]=tmp;
}
w=-w;
}
int k2;
while(d[j][i]!=0){
k2=d[i][i]/d[j][i];
for(int k=1;k<=k1;k++){
d[i][k]=(d[i][k]-k2*d[j][k]%mod+mod)%mod;
}
for(int k=1;k<=k1;k++){
int tmp;
tmp=d[j][k];
d[j][k]=d[i][k];
d[i][k]=tmp;
}
w=-w;
}
}
}
int ans=1;
for(int i=1;i<=k1;i++){
ans*=d[i][i];
ans%=mod;
}
ans=(ans*w+mod)%mod;
return ans;
}
}jz;
signed main(){
scanf("%lld%lld%lld",&n,&m,&t);
if(t==0){
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
if(u!=n){
jz.d[u][u]=(jz.d[u][u]+w)%mod;
if(v!=n){
jz.d[u][v]=(jz.d[u][v]-w+mod)%mod;
jz.d[v][u]=(jz.d[v][u]-w+mod)%mod;
}
}
if(v!=n){
jz.d[v][v]=(jz.d[v][v]+w)%mod;
}
}
jz.k1=n-1;
int ans=jz.qz();
printf("%lld\n",ans);
}
else{
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
if(u!=1){
//jz.d[u][u]=(jz.d[u][u]+w)%mod;
if(v!=1){
jz.d[u-1][v-1]=(jz.d[u-1][v-1]-w+mod)%mod;
//jz.d[v][u]=(jz.d[v][u]-w+mod)%mod;
}
}
if(v!=1){
jz.d[v-1][v-1]=(jz.d[v-1][v-1]+w)%mod;
}
}
jz.k1=n-1;
int ans=jz.qz();
printf("%lld\n",ans);
}
return 0;
}