注:這是我什么都不會的時候寫的(東抄西抄拼起來),有很多鍋,建議不要看了 QAQ。
一、行列式
1. 定義
二階行列式:\( \begin{vmatrix} a_{11} & a_{12}\\ a_{21} & a_{22} \end{vmatrix} =a_{11}a_{22}-a_{21}a_{12} \)。即主對角線的乘積減去副對角線的乘積。
三階行列式:三階行列式的計算方法與二階行列式類似。
\( \begin{vmatrix} a_{11} & a_{12} & a_{13}\\ a_{21} & a_{22} & a_{23}\\ a_{31} & a_{32} & a_{33} \end{vmatrix}=a_{11}a_{22}a_{33}+a_{12}a_{23}a_{31}+a_{13}a_{21}a_{32}-a_{13}a_{22}a_{31}-a_{12}a_{21}a_{33}-a_{11}a_{23}a_{32} \)
\(n\) 階行列式:
由 \(n^2\) 個元素構成的 \(n\) 階行列式為:
\( \left| a_{ij}\right|_n= \begin{vmatrix} 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{vmatrix} =\sum\limits_{j_1 j_2 \cdots j_n}(-1)^{r(j_1 j_2 \cdots j_n)}a_{1j_1}a_{2j_2}\cdots a_{nj_n} \)
其中 \(j_1 j_2 \cdots j_n\) 是 \(1,2,\cdots,n\) 的一個排列。
\(r(j_1 j_2 \cdots j_n)\) 是排列的逆序對數量。
2. 性質
設 \(n\) 階行列式為 \(D=\left| a_{ij}\right| _n\),稱行列式 \(\left| a_{ji}\right| _n\)(行與列交換一下,即 \(a_{ij}\) 變為 \(a_{ji}\))為 \(D\) 的轉置行列式,記作 \(D^T\)。
一些性質:
-
\(D^T=D\)。
-
交換行列式 \(D\) 中任意兩行得到 \(D_1\),則 \(D_1=-D\)。
-
行列式 \(D\) 中某一行都乘上 \(k\) 得到 \(D_1\),則 \(D_1=kD\)。
-
行列式 \(D\) 中的某一行加上另一行的 \(c\) 倍(對應相加,其中 \(c\) 為一個常數)得到 \(D_1\),則 \(D_1=D\)。
3. 代數余子式
余子式與代數余子式:
在 \(n\)(\(n>1\)) 階行列式 \(D=|a_{ij}|\) 中,划去元素 \(a_{ij}\) 所在的第 \(i\) 行和第 \(j\) 列,余下的元素按原來順序組成的 \(n-1\) 階行列式,稱為 \(D\) 中元素 \(a_{ij}\) 的余子式,即為 \(M_{ij}\)。
\(a_{ij}\) 的余子式 \(M_{ij}\),在它前面添加符號 \((-1)^{i+j}\) 后,稱為 \(a_{ij}\) 在 \(D\) 中的代數余子式,記為 \(A_{ij}\),即 \(A_{ij}=(-1)^{i+j} M_{ij}\)。
\(n\) 階行列式 \(D=|a_{ij}|\) 等於它的任意一行(列)中各元素與其對應的代數余子式乘積的和,即 \(D=a_{i1}A_{i1}+a_{i2}A_{i2}+\cdots+a_{in}A_{in}\) 或 \(D=a_{1j}A_{1j}+a_{2j}A_{2j}+\cdots+a_{nj}A_{nj}\)(\(i,j=1,2,\cdots,n\))。
舉個栗子:
\(D=\begin{vmatrix}1 & 2 & 3\\0 & 2 & 0\\2 & 3 & 1\end{vmatrix}=0\cdot A_{21}+2\cdot A_{22}+0\cdot A_{23}\)
\(=2\cdot A_{22}=2\cdot (-1)^{2+2}\begin{vmatrix}1 & 3\\2 & 1\end{vmatrix}=2\times (-5)=-10\)
\(k\) 階子式的余子式與代數余子式:
在 \(n\) 階行列式 \(D\) 中划去任意選定的 \(k\) 行、\(k\) 列后(\(0<k<n\)),位於這些行和列交叉處的 \(k^2\) 個元素按原來順序組成的 \(k\) 階行列式 \(M\),稱為 \(M\) 是行列式 \(D\) 的一個 \(k\) 階子式。
在 \(n\) 階行列式 \(D\) 中划去任意選定的 \(k\) 行、\(k\) 列后(\(0<k<n\)),余下的元素按原來順序組成的 \(n-k\) 階行列式 \(N\),稱為 \(N\) 是 \(k\) 階子式 \(M\) 的余子式。
如果 \(k\) 階子式 \(M\) 在行列式 \(D\) 中的行和列的標號分別為 \(i_1,i_2,\cdots,i_k\) 和 \(j_1,j_2,\cdots,j_k\),則在 \(M\) 的余子式 \(N\) 前面添加符號 \((-1)^{(i_1+i_2+\cdots+i_k)+(j_1+j_2+\cdots+j_k)}\) 后,所得到的 \(n-k\) 階行列式,稱為行列式 \(D\) 的 \(k\) 階子式 \(M\) 的代數余子式,記作 \(A\)。即 \(A=(-1)^{(i_1+i_2+\cdots+i_k)+(j_1+j_2+\cdots+j_k)}N\)。
(主子式:去掉的行和列編號相同。)
4. 范德蒙德行列式
\(D_n=\begin{vmatrix} 1 & 1 & \cdots & 1 \\ x_1 & x_2 & \cdots & x_n \\ x_1^2 & x_2^2 & \cdots & x_n^2\\ \vdots & \vdots & \ddots & \vdots \\ x_1^{n-1} & x_2^{n-1} & \cdots & x_n^{n-1} \end{vmatrix} =\prod\limits_{1\leq i<j\leq n}(x_j-x_i)\)
用數學歸納法證明。
當 \(n=2\) 時,\(D_2=\begin{vmatrix} 1 & 1 \\ x_1 & x_2\end{vmatrix}=x_2-x_1\),顯然結論成立。
假設該結論對 \(n-1\) 階范德蒙行列式成立,即:
\(D_{n-1}=\begin{vmatrix} 1 & 1 & \cdots & 1 \\ x_1 & x_2 & \cdots & x_{n-1} \\ x_1^2 & x_2^2 & \cdots & x_{n-1}^2\\ \vdots & \vdots & \ddots & \vdots \\ x_1^{n-2} & x_2^{n-2} & \cdots & x_{n-1}^{n-2} \end{vmatrix}=\prod\limits_{1\leq i<j\leq {n-1}}(x_j-x_i)\)
考慮 \(n\) 階范德蒙行列式的情形。
從第 \(n\) 行開始,自下而上地依次讓每一行減去它上一行的 \(x_n\) 倍。
\(D_n=\begin{vmatrix} 1 & 1 & \cdots & 1 \\ x_1-x_n & x_2-x_n & \cdots & 0 \\ x_1(x_1-x_n) & x_2(x_2-x_n) & \cdots & 0\\ \vdots & \vdots & \ddots & \vdots \\ x_1^{n-2}(x_1-x_n) & x_2^{n-2}(x_2-x_n) & \cdots & 0 \end{vmatrix}\)
\(=(-1)^{n+1}\begin{vmatrix}x_1-x_n & x_2-x_n & \cdots & x_{n-1}-x_n \\ x_1(x_1-x_n) & x_2(x_2-x_n) & \cdots & x_{n-1}(x_{n-1}-x_n)\\ \vdots & \vdots & \ddots & \vdots \\ x_1^{n-2}(x_1-x_n) & x_2^{n-2}(x_2-x_n) & \cdots & x_{n-1}^{n-2}(x_{n-1}-x_n) \end{vmatrix}\)
注意到行列式的一個性質:行列式 \(D\) 中某一行都乘上 \(k\) 得到 \(D_1\),則 \(D_1=kD\)。列也是如此。那么就可以提取每一列的公因式。
\(D_n=(-1)^{n+1}(x_1-x_n)(x_2-x_n)\cdots (x_{n-1}-x_n)\begin{vmatrix}1 & 1 & \cdots & 1 \\ x_1 & x_2 & \cdots & x_{n-1}\\ \vdots & \vdots & \ddots & \vdots \\ x_1^{n-2} & x_2^{n-2} & \cdots & x_{n-1}^{n-2} \end{vmatrix}\)
\(=(-1)^{n+1}(x_1-x_n)(x_2-x_n)\cdots (x_{n-1}-x_n)D_{n-1}\)
\(=(-1)^{n+1}(x_1-x_n)(x_2-x_n)\cdots (x_{n-1}-x_n)\prod\limits_{1\leq i<j\leq {n-1}}(x_j-x_i)\)
\(=\prod\limits_{1\leq i<j\leq n}(x_j-x_i)\)
二. 矩陣
1. 定義
由 \(m\times n\) 個數排成的 \(m\) 行 \(n\) 列的矩陣數表
\(\begin{bmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\vdots&\vdots&\ddots&\vdots\\a_{m1}&a_{m2}&\cdots&a_{mn}\end{bmatrix}\)
稱為一個 \(m\times n\) 的矩陣。
矩陣的運算:加法、數乘、乘法、轉置。此處略。
2. 伴隨矩陣
設 \(A=(a_{ij})_n\) 為 \(n\) 階矩陣,\(A_{ij}\) 為 \(A\) 中元素 \(a_{ij}\) 的代數余子式,\(i,j=1,2,\cdots,n\),則稱矩陣
\(\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}\)
為 \(A\) 的伴隨矩陣,記作 \(A^*\)。可以證明 \(AA^*=A^*A=\left|A\right|E\)。
3. 求逆矩陣
設 \(A\) 是 \(n\) 階矩陣,若存在 \(n\) 階矩陣 \(B\),使得 \(AB=BA=E\),則稱矩陣 \(A\) 可逆,\(B\) 是 \(A\) 的逆矩陣,記作 \(B=A^{-1}\)。
\(A^{-1}=\frac{1}{|A|}A^*\)
構造一個 增廣矩陣 \(W=[A\mid E]\),對 \(W\) 初等變換,變成 \(W=[E\mid B]\),可以確定 \(A^{-1}=B\)。
初等變換包括:
-
交換的矩陣的兩行(列)。
-
矩陣某一行(列)的元素乘同一個不等於 \(0\) 的數。
-
矩陣某一行(列)的元素加上另一行(列)對應元素的相同倍數。
三、矩陣加速遞推
HDU 4549 M斐波那契數列
題目大意:\(M\) 斐波那契數列 \(F_n\) 是一種整數數列。\(F_0=a,F_1=b,F_n=F_{n-1}\times F_{n-2}\)(\(n>1\))。給定 \(a,b,n\),求 \(F_n\) 的值。對 \(10^9+7\) 取模。
Solution:
\(F_0=a,F_1=b,F_n=F_{n-1}\times F_{n-2}\),那么 \(F_2=ab,F_3=ab^2,F_4=a^2b^3,F_5=a^3b^5,F_6=a^5b^8,\cdots,F_n=a^{f_{n-1}\ \ }b^{f_n}\)。其中 \(f_i\) 為斐波那契數列的第 \(i\) 項。問題轉化為如何快速求斐波那契數列。
考慮構造出轉移矩陣 \(\begin{bmatrix}a_1 & a_2\\a_3 & a_4\end{bmatrix}\),使得:
\(\begin{bmatrix}f_{n-1} & f_n\end{bmatrix}\times \begin{bmatrix}a_1 & a_2\\a_3 & a_4\end{bmatrix}=\begin{bmatrix}f_n & f_{n+1}\end{bmatrix}\)
\(f_{n-1}\times a_1+f_n\times a_3=f_n\),\(f_{n-1}\times a_2+f_n\times a_4=f_{n+1}\),顯然 \(a_1=0,a_2=1,a_3=1,a_4=1\)。
即轉移矩陣為 \(\begin{bmatrix}0 & 1\\1 & 1\end{bmatrix}\)。
\(\begin{bmatrix}f_n & f_{n+1}\end{bmatrix}=\begin{bmatrix}f_{n-1} & f_n\end{bmatrix}\times \begin{bmatrix}0 & 1\\1 & 1\end{bmatrix}=\begin{bmatrix}f_{n-2} & f_{n-1}\end{bmatrix}\times \begin{bmatrix}0 & 1\\1 & 1\end{bmatrix}^2=\begin{bmatrix}f_0 & f_1\end{bmatrix}\times \begin{bmatrix}0 & 1\\1 & 1\end{bmatrix}^n\)
\(F_n=(a^{f_{n-1}\ \ }\bmod m)\times(b^{f_n}\bmod m)\),其中 \(m=10^9+7\)。則 \(f_n\) 和 \(f_{n-1}\) 會非常大,需要用費馬小定理降冪。
\(a^p\bmod m=a^{p\bmod (m-1)}\)。所以 \(F_n=a^{(f_{n-1}\ \bmod (m-1))}b^{(f_n\bmod (m-1))}\)。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=2,mod=1e9+7; int x,y,n,f[N][N],k1,k2; void mul(int x[N][N],int y[N][N]){ //矩陣乘法 int c[N][N]; memset(c,0,sizeof(c)); for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) c[i][j]=(c[i][j]+x[i][k]*y[k][j])%(mod-1); memcpy(x,c,sizeof(c)); } int ksm(int x,int n,int mod){ //快速冪 int ans=mod!=1; for(x%=mod;n;n>>=1,x=x*x%mod) if(n&1) ans=ans*x%mod; return ans; } signed main(){ while(~scanf("%lld%lld%lld",&x,&y,&n)){ int a[N][N]={{0,1},{1,1}}; //轉移矩陣 memset(f,0,sizeof(f)); for(int i=0;i<N;i++) f[i][i]=1; //構造單位矩陣。單位矩陣起着特殊的作用,如同數的乘法中的 1。 for(;n;n>>=1,mul(a,a)) //矩陣快速冪 if(n&1) mul(f,a); k1=f[0][0],k2=f[0][1]; //k1=f[n],k2=f[n+1]。其中 f[i] 為斐波那契數列第 i 項。 printf("%lld\n",ksm(x,k1,mod)*ksm(y,k2,mod)%mod); } return 0; }
多階線性遞推
例如 \(f_1=f_2=0\),\(f_n=7f_{n-1}+6f_{n-2}+5n+4\times 3^n\)。這里直接給出轉移:
\(\begin{bmatrix}f_n & f_{n-1} & n & 3^n &1\end{bmatrix}\times \begin{bmatrix}7 & 1 & 0 & 0 & 0\\6 & 0 & 0 & 0 & 0\\5 & 0 & 1 & 0 & 0\\12 & 0 & 0 & 3 & 0\\5 & 0 & 1 & 0 & 1\end{bmatrix}=\begin{bmatrix}f_{n+1} & f_n & n+1 & 3^{n+1} &1\end{bmatrix}\)
四、高斯消元
1. 基本思想
高斯消元是一種求解線性方程組的方法。求解這種方程組的步驟可概括成對 增廣矩陣 的三類操作:
-
用一個非零的數乘某一行。
-
把其中一行的若干倍加到另一行上。
-
交換兩行的位置。
我們把這三類操作成為矩陣的“初等行變換”。同理,我們也可以定義矩陣的“初等列變換”。
通過初等行變換把增廣矩陣變為簡化階梯形矩陣的線性方程組求解算法就是高斯消元算法。高斯消元的算法思想就是,對於每個未知量 \(x_i\),找到一個 \(x_i\) 的系數非零,但 \(x_1\sim x_{i-1}\) 的系數都是零的方程(當然也有可能找不到),然后用初等行變換把其他方程的 \(x_i\) 的系數全部消成零。
另外,在高斯消元的過程中,可能會遇到各種各樣的特殊情形。
當消元后出現了 \(0=b\) 這樣的方程,其中 \(b\) 是一個非零常數,則說明原方程組無解。
當消元后某一方程有不只一個系數非零,那么原方程組有無數解。
2. 舉個栗子
\(\left\{\begin{aligned} x_1+2x_2-x_3&=-6 \notag \\ 2x_1+x_2-3x_3&=-9 \notag \\ -x_1-x_2+2x_3&=7 \notag \end{aligned}\right.\)
先把它寫成增廣矩陣。
\(\left\{\begin{aligned} x_1+2x_2-x_3&=-6 \notag \\ 2x_1+x_2-3x_3&=-9 \notag \\ -x_1-x_2+2x_3&=7 \notag \end{aligned}\right. \Rightarrow \begin{bmatrix}\begin{array}{ccc|c}1 & 2 & -1 & 3\\0 & -3 & -1 & 3\\-1 & -1 & 2 & 7\end{array}\end{bmatrix}\)
然后用若干次初等行變換求解上面的方程組,過程如下:
\(\begin{bmatrix}1 & 2 & -1 & -6\\2 & 1 & -3 & -9\\-1 & -1 & 2 & 7\end{bmatrix}\stackrel{r_2-2r_1}{\Longrightarrow}\begin{bmatrix}1 & 2 & -1 & -6\\0 & -3 & -1 & 3\\-1 & -1 & 2 & 7\end{bmatrix}\stackrel{r_3+r_1}{\Longrightarrow}\begin{bmatrix}1 & 2 & -1 & -6\\0 & -3 & -1 & 3\\0 & 1 & 1 & 1\end{bmatrix}\)
\(\stackrel{swap(r_2,r_3)}{\Longrightarrow}\begin{bmatrix}1 & 2 & -1 & -6\\0 & 1 & 1 & 1\\0 & -3 & -1 & 3\end{bmatrix} \stackrel{r_3+3r_2}{\Longrightarrow}\begin{bmatrix}1 & 2 & -1 & -6\\0 & 1 & 1 & 1\\0 & 0 & 2 & 6\end{bmatrix} \stackrel{r_3\times 0.5}{\Longrightarrow}\begin{bmatrix}1 & 2 & -1 & -6\\0 & 1 & 1 & 1\\0 & 0 & 1 & 3\end{bmatrix}\)
最后得到的矩陣被稱為“階梯形矩陣”,它的系數矩陣部分被稱為“上三角矩陣”。這個矩陣表達的信息是:
\(\begin{bmatrix}\begin{array}{ccc|c}1 & 2 & -1 & -6\\0 & 1 & 1 & 1\\0 & 0 & 1 & 3\end{array}\end{bmatrix} \Rightarrow \left\{\begin{aligned} x_1+2x_2-x_3&=-6 \notag \\ x_2+x_3&=1 \notag \\ x_3&=3 \notag \end{aligned}\right.\)
因此,我們已經知道了最后一個未知量的值,從下往上依次代回方程組,即可得到每個未知量的解。事實上,該矩陣也可以進一步化簡:
\(\begin{bmatrix}1 & 2 & -1 & -6\\0 & 1 & 1 & 1\\0 & 0 & 1 & 3\end{bmatrix}\stackrel{r_1+r_3,r_2-r_3}{\Longrightarrow} \begin{bmatrix}1 & 2 & 0 & -3\\0 & 1 & 0 & -2\\0 & 0 & 1 & 3\end{bmatrix}\stackrel{r_1-2r_2}{\Longrightarrow} \begin{bmatrix}\begin{array}{ccc|c}1 & 0 & 0 & 1\\0 & 1 & 0 & -2\\0 & 0 & 1 & 3\end{array}\end{bmatrix}\)
最后得到的矩陣被稱為“簡化階梯形矩陣”,它的系數矩陣部分是一個“對角矩陣”。該矩陣已經直接給出了方程的解。
//Luogu P3389 #include<bits/stdc++.h> #define int long long using namespace std; const int N=110; int n; double a[N][N]; signed main(){ scanf("%lld",&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++){ //消第 i 個元 int p=i; for(int j=i;j<=n;j++) //找到 x[i] 的系數不為 0 的一個方程 if(fabs(a[j][i])>1e-8){p=j;break;} for(int j=1;j<=n+1;j++) swap(a[i][j],a[p][j]); if(fabs(a[i][i])<1e-8) puts("No Solution"),exit(0); //不存在唯一解 for(int j=1;j<=n;j++){ //消去其他方程的 x[i] 的系數 if(i==j) continue; double x=a[j][i]/a[i][i]; for(int k=i;k<=n+1;k++) a[j][k]-=a[i][k]*x; } } for(int i=1;i<=n;i++) printf("%.2lf\n",a[i][n+1]/a[i][i]); return 0; }
五、生成樹計數
1. 前置概念
\(G\) 的度數矩陣 \(D[G]\) 是一個 \(n\times n\) 的矩陣,並且滿足:當 \(i\neq j\) 時,\(d_{ij}=0\);當 \(i=j\) 時,\(d_{ij}\) 等於 \(v_i\) 的度數。
\(G\) 的鄰接矩陣 \(A[G]\) 也是一個 \(n\times n\) 的矩陣,並且滿足:如果 \(v_i\)、\(v_j\) 之間有邊直接相連,則 \(a_{ij}=1\),否則為 \(0\)。
定義 \(G\) 的 Kirchhoff 矩陣(也稱為拉普拉斯算子)\(C[G]\) 為 \(C[G]=D[G]-A[G]\)。
2. Matrix-Tree 定理
\(G\) 的所有不同的生成樹的個數等於其 Kirchhoff 矩陣 \(C[G]\) 任何一個 \(n-1\) 階主子式的行列式的絕對值。
證明省略。但在證明中我們用到了一個矩陣,稱為關聯矩陣。
關聯矩陣是一個 \(n\) 行 \(m\) 列的矩陣,行對應點而列對應邊。
關聯矩陣滿足,如果存在一條邊 \(e=\{v_i,v_j\}\),那在 \(e\) 所對應的列中,\(v_i\) 和 \(v_j\) 所對應的那兩行,一個為 \(1\)、另一個為 \(-1\),其他的均為 \(0\)。至於哪個是 \(1\) 哪個是 \(-1\) 並不重要。
我們令關聯矩陣為 \(B\),考慮 \(BB^T\),可以發現 \(C=BB^T\)。
//SP104 HIGH - Highways #include<bits/stdc++.h> #define int long long using namespace std; const int N=20; int t,n,m,x,y; double a[N][N],ans; void Gauss(int n){ //高斯消元 for(int i=1;i<=n;i++){ int p=i; for(int j=i;j<=n;j++) if(fabs(a[j][i])>1e-8){p=j;break;} for(int j=1;j<=n+1;j++) swap(a[i][j],a[p][j]); if(fabs(a[i][i])<1e-8) return ; for(int j=1;j<=n;j++){ if(i==j) continue; double x=a[j][i]/a[i][i]; for(int k=i;k<=n+1;k++) a[j][k]-=a[i][k]*x; } } } signed main(){ scanf("%lld",&t); while(t--){ memset(a,0,sizeof(a)),ans=1; scanf("%lld%lld",&n,&m); for(int i=1;i<=m;i++){ scanf("%lld%lld",&x,&y); a[x][x]++,a[y][y]++; a[x][y]--,a[y][x]--; //Kirchhoff 矩陣 } n--,Gauss(n); //利用高斯消元將 Kirchhoff 矩陣消為對角矩陣 for(int i=1;i<=n;i++) ans=ans*a[i][i]; printf("%.0lf\n",fabs(ans)); //高斯消元后,由於已經消成了對角矩陣,所以對角線乘積的絕對值就是答案 } return 0; }
六、習題
矩陣加速遞推:
- BZOJ 4887「TJOI 2007」可樂
- LOJ 6208 樹上詢問(這個代碼 過了是什么情況 ¿)
高斯消元:
- HDU 2408 String Equations
- BZOJ 3143「HNOI 2013」游走
生成樹計數:
- HDU 4408 Minimum Spanning Tree
- HDU 6836 Expectation