行列式
行列式的概念
记一个 n × n n \times n n×n行列式为:
D = ∥ a 11 a 12 . . . a 1 n a 21 a 22 . . . a 2 n . . . . . . . . . . . . a n 1 a n 2 . . . a n n ∥ D=\left\| \begin{matrix} a_{11} & a_{12} & ... & a_{1n} \\ a_{21} & a_{22} & ... & a_{2n} \\ ... & ... & ... & ... \\ a_{n1} & a_{n2} & ... & a_{nn} \end{matrix} \right\| D=∥∥∥∥∥∥∥∥a11a21...an1a12a22...an2............a1na2n...ann∥∥∥∥∥∥∥∥
计算结果为:
∑ ( − 1 ) t a 1 p 1 a 2 p 2 a 3 p 3 . . . a n p n \sum (-1)^t a_{1p_1}a_{2p2}a_{3p_3}...a_{np_n} ∑(−1)ta1p1a2p2a3p3...anpn
其中t是排列 p 1 , p 2 . . . p n p_1,p_2...p_n p1,p2...pn的逆序对个数。
也可以记忆一下,二阶行列式是 a 11 a 22 − a 12 a 21 a_{11}a_{22}-a_{12}a_{21} a11a22−a12a21。
行列式的求法
性质1:交换行列式的两行,行列式的值会变成它的相反数。
证明:比如交换第i行和第j行,那么行列式由 ∑ ( − 1 ) t 1 . . . a i p i . . . a j p j . . . \sum (-1)^{t_1}...a_{ip_i}...a_{jp_j}... ∑(−1)t1...aipi...ajpj...变成了 ∑ ( − 1 ) t 2 . . . a j p j . . . a i p i . . . \sum (-1)^{t_2}...a_{jp_j}...a_{ip_i}... ∑(−1)t2...ajpj...aipi...,相当于把每一个排列都交换了两个元素,这样这个排列的逆序对数奇偶性一定会改变:
若只看每个数前面比其大的数,如果 p i > p j p_i>p_j pi>pj,那么在 p j p_j pj前面比其大的数的个数减1,而原来在 p j p_j pj前面,现在不在的比 p j p_j pj大的数,如果比 p i p_i pi大,则无影响。否则它们前面失去了一个比它们大的 p i p_i pi,个数都减1,而 p j p_j pj失去了它们,个数也减一,相当于奇偶性不变. p i < p j p_i<p_j pi<pj可以视作考虑每个数后面的比其小的数的个数,也同理。
那么每个
t
1
t_1
t1一定和
t
2
t_2
t2奇偶性不同。
性质2:某一行乘以一个相同的数,加在另一行上,行列式不变。
证明:相当于变成 ∑ x ( − 1 ) t a 1 p 1 a 2 p 2 a 3 p 3 . . . a n p n \sum x(-1)^t a_{1p_1}a_{2p2}a_{3p_3}...a_{np_n} ∑x(−1)ta1p1a2p2a3p3...anpn,由于行列式系数为正的项数和为负的相等,所以答案不变。
性质3:
D
=
∥
a
11
0
0
.
.
.
0
a
21
a
22
0
.
.
.
0
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
a
n
1
a
n
2
a
n
3
.
.
.
a
n
n
∥
=
∏
a
i
i
D=\left\| \begin{matrix} a_{11} & 0 &0 &... & 0 \\ a_{21} & a_{22} &0 &... & 0 \\ ... & ... & ... & ...&... \\ a_{n1} & a_{n2} & a_{n3} &... & a_{nn} \end{matrix} \right\|=\prod a_{ii}
D=∥∥∥∥∥∥∥∥a11a21...an10a22...an200...an3............00...ann∥∥∥∥∥∥∥∥=∏aii
性质4:某一行同时乘以一个相同的数,行列式也要乘以那个数。
由上述性质,我们可以得到一种类似于高斯消元的求矩阵行列式的方法(例题:spoj-DETER3):
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int n;LL p,a[205][205];
LL work() {
LL re=1;
for(int j=1;j<=n;++j) {
int id=j; while(id<=n&&!a[id][j]) ++id;
if(id>n) return 0;
if(id!=j) swap(a[j],a[id]),re=-re;
for(int i=j+1;i<=n;++i)
while(a[i][j]) {
LL t=a[j][j]/a[i][j];
for(int k=j;k<=n;++k) a[j][k]=(a[j][k]-a[i][k]*t)%p;
swap(a[j],a[i]),re=-re;
}
re=(re*a[j][j])%p;
}
return (re%p+p)%p;
}
int main() {
while(~scanf("%d%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;
printf("%lld\n",work());
}
return 0;
}
按行(列)展开
余子式:一个行列式去掉它的第i行和第j列得到的行列式称为它的余子式,记做 M i j M_{ij} Mij
代数余子式: a i j = ( − 1 ) i + j M i j a_{ij}=(-1)^{i+j}M_{ij} aij=(−1)i+jMij
行列式等于它的任一行(列)各元素与其对应的代数余子式乘积之和,即
D = a i 1 A i 1 + a i 2 A i 2 + . . . + a i n A i n ( i = 1 , 2... n ) D=a_{i1}A_{i1}+a_{i2}A_{i2}+...+a_{in}A_{in} (i=1,2...n) D=ai1Ai1+ai2Ai2+...+ainAin(i=1,2...n)
或
D = a 1 j A 1 j + a 2 j A 2 j + . . . + a n j A n j ( j = 1 , 2... n ) D=a_{1j}A_{1j}+a_{2j}A_{2j}+...+a_{nj}A_{nj}(j=1,2...n) D=a1jA1j+a2jA2j+...+anjAnj(j=1,2...n)
这就是行列式按行(列)展开法则,可以简化行列式计算.
伴随矩阵:第i行j列为矩阵A的 a i j a_{ij} aij的矩阵被称为A的伴随矩阵,记做 A ∗ A^{*} A∗
应用
对于两个棋子的情况,如果路径不相交,则方案数为 C b 1 − a 1 + n − 1 n − 1 × C b 2 − a 2 + n − 1 n − 1 C_{b_1-a_1+n-1}^{n-1} \times C_{b_2-a_2+n-1}^{n-1} Cb1−a1+n−1n−1×Cb2−a2+n−1n−1,如果相交,则可以看其最下面的一个交点,交换该交点后的路径,可以把一个相交的关系看作两条从 a 1 a_1 a1到 b 2 b_2 b2和从 a 2 a_2 a2到 b 1 b_1 b1的路径,方案数为 C b 2 − a 1 + n − 1 n − 1 × C b 1 − a 2 + n − 1 n − 1 C_{b_2-a_1+n-1}^{n-1} \times C_{b_1-a_2+n-1}^{n-1} Cb2−a1+n−1n−1×Cb1−a2+n−1n−1。
而多条路径相交的情况,则考虑容斥。那么相当于终点被打乱了,成为一个排列。而奇排列(逆序数为奇数的排列)一定是由一个从1到n的排列经过奇数次元素两两交换得到的(自己证),因此原问题可以转化为一个行列式问题。
此外,交这道题千万不要用C++,请用G++,不然会超时(别问我是怎么知道的)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const LL mod=1000000007;
int n,m,T;LL inv[200005],fac[200005];
int a[105],b[105];LL t[105][105];
LL ksm(LL x,LL y) {
LL re=1;
for(;y;x=x*x%mod,y>>=1) if(y&1) re=re*x%mod;
return re;
}
void init() {
fac[0]=inv[0]=1;
for(LL i=1;i<=200000;++i) fac[i]=fac[i-1]*i%mod;
inv[200000]=ksm(fac[200000],mod-2);
for(int i=200000-1;i>=1;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
LL C(LL d,LL u) {
if(u>d) return 0;
return fac[d]*inv[u]%mod*inv[d-u]%mod;
}
LL work() {
LL re=1;
for(int j=1;j<=m;++j) {
int id=j; while(id<=m&&!t[id][j]) ++id;
if(id>m) return 0;
if(id!=j) swap(a[id],a[j]),re=-re;
LL kl=ksm(t[j][j],mod-2);
for(int i=j+1;i<=m;++i)
if(t[i][j]) {
re=re*kl%mod;//第i行乘t[j][j],相当于将行列式的值扩大了t[j][j]倍
for(int k=m;k>=j;--k)
t[i][k]=(t[i][k]*t[j][j]%mod-t[j][k]*t[i][j]%mod+mod)%mod;
}
re=(re*t[j][j])%mod;
}
return (re%mod+mod)%mod;
}
int main() {
init(),scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i) scanf("%d",&b[i]);
for(int i=1;i<=m;++i)
for(int j=1;j<=m;++j) t[i][j]=C(n-1+b[i]-a[j],n-1);
printf("%lld\n",work());
}
return 0;
}
矩阵相关
矩阵的逆
使得矩阵
A
A
−
1
=
I
AA^{-1}=I
AA−1=I(I是单位矩阵)的
A
−
1
A^{-1}
A−1为
A
A
A的逆矩阵,且
A
−
1
A
=
E
A^{-1}A=E
A−1A=E
A
−
1
=
1
∣
A
∣
A
∗
A^{-1}=\frac{1}{|A|} A^{*}
A−1=∣A∣1A∗
矩阵的秩
在矩阵中任取 k k k行 k k k列,这些行列的交点处的元素取出来,不改变它们的相对位置,构成的一个行列式,称为该矩阵的 k k k阶余子式。如果一个矩阵A的任一 r r r阶子式不为 0 0 0,而所有 r + 1 r+1 r+1阶子式为 0 0 0,则 r r r称为 A A A的秩。
矩阵的秩等于阶数最大的非零余子式的阶数。
矩阵的特征值
若对于矩阵 A A A,存在一个向量 α \alpha α和一个实数 λ \lambda λ使得 A α = λ α A\alpha=\lambda \alpha Aα=λα则称 α \alpha α为 A A A的特征向量, λ \lambda λ为 A A A的特征值。
那么根据 ∣ λ I − A ∣ = 0 |\lambda I-A|=0 ∣λI−A∣=0可以解方程求出特征值,进而求出特征向量。