今天測試的時候出了【NOI2007】生成樹計數,而後完全被提示坑了,用行列式只能作\(40\%\)的數據,正確的解法應該是矩陣乘法,但這個不在文章的討論範圍,本文主要討論如何用高斯消元求解行列式php
要知道什麼是行列式?(百度、維基)
其實我也不太懂,只知道是\(n \times n\)矩陣的標量。
只要瞭解了行列式的性質,就能夠求值了。測試
一個行列式用:$ \begin{vmatrix} A \end{vmatrix} \(或\)\det(A)$表示,例如url
暫且把\(\det(A)\)的第\(i\)行第\(j\)列的數記做\(a_{ij}\),行列式的值爲spa
\(S_n \text 是指全排列的集合,\sigma \text 就是一個全排列, 若是\sigma的逆序對對數爲偶數,則sgn=1,不然=-1\)
例如:(上面的例子)code
全排列 | 逆序對對數 | \(sgn\) | value |
---|---|---|---|
1 2 3 | 0 | 1 | \(1 \times 5 \times 0=0\) |
1 3 2 | 1 | -1 | \(-1 \times 6 \times 8=-48\) |
2 1 3 | 1 | -1 | \(-2 \times 4 \times 0=0\) |
2 3 1 | 2 | 1 | \(2 \times 6 \times 7=84\) |
3 1 2 | 2 | 1 | $3 \times 4 \times 8=96 $ |
3 2 1 | 3 | -1 | \(-3 \times 5 \times 7=-105\) |
總和 | \(0-48+0+84+96-105=27\) |
要知道行列式的一些性質,才能更好得求值
下面的相等均值行列式的值相等。blog
若 \(b_{ij}=a_{ji}\) ,則 \(\det(B)\) 爲 \(\det(A)\) 的轉置行列式
例如:\(\det(A)\)的轉置行列式爲ip
顯然,對於原行列式的任意兩個數,在新行列式的順逆序關係沒有變。例如 $a_{ij} , a_{pq} (i < p) $ ,在 \(\det(B)\) 爲 \(b_{ji},b_{qp}\)
若 \(j < q\) ,則兩數在 \(\det(A)\) 中爲順序,在 \(\det(B)\) 中也是順序 \((j < q,i < q)\) .
若 \(j > q\) ,則兩數在 \(\det(A)\) 中爲逆序,在 \(\det(B)\) 中也是逆序 \((q < j,p > i)\)
證畢。get
交換兩行後,順逆序關係會相反,例如 \(a_{ij},a_{pq}(i < p,j < q)\) ,交換後變成 \(a_{iq},a_{pj}(i < p,q > j)\) ,當 \(j > q\) 時也同樣。因此選擇了這一對數的全排列的值都要乘 \((-1)\) ,兩行的數對可取盡整個行列式的全排列,因此行列式的值乘\((-1)\)。
證畢。string
由於行列式的值是全排列的值相加,而某一行的全部元素都乘以k至關於每一個全排列的值都乘以k,因此至關於整個行列式乘以k.
證畢。it
設\(a_i=ka_j\),將\(a_i變成a_j\),則整個行列式的值乘以k,行列式中有相等的兩行\(a_j\),交換相等的兩行\(a_j\),行列式的值取相反數,但行列式的元素並無改變,因此行列式的值爲0.乘k依然爲0.
證畢。
舉個例子:
這個性質由乘法分配律能夠容易得出,自行腦補。
設\(a_i,a_j,\det(C)=\det(A)且c_j=kc_i+c_j,\det(B)=\det(A)且b_j=b_i\),則根據性質五得
\(\det(C)=\det(A)+k\det(B)\)
根據性質四得\(k\det(B)=0\),因此\(\det(C)=\det(A)\)
證畢。
根據性質六,就能夠用高斯消元解行列式了。
高斯消元沒什麼好講的,就說一下要注意的細節吧。
一、高斯消元交換兩行時答案要除以\((-1)\).(性質二)
二、假設處理到第\(i\)個方程,通常的高斯消元是用第\(i\)個方程減去第\(j(j>i)\)個方程,所得的答案做爲新的第\(j\)個方程,但求行列式的時候要用第\(j\)行減去第\(i\)行,所得答案做爲新的第\(j\)行。由於方式一至關於某一行乘\((-1)\),另外一行加到這一行上,這並不符合性質六。
而後答案就是主對角線的乘積。由於雖然高斯消元后的行列式爲一個倒三角形,但能夠按列來消,最後就只剩下主對角線了。或者這樣說,行列式爲一個倒三角,用最原始的算值方法,最後一行必定要選第n個數,倒數第二行要選第n-1個數,以此類推,就把主對角線都乘起來了,若是不這樣選,答案就是0,對最終答案沒用貢獻。
貼代碼
#include <cstdio> #include <cmath> #include <algorithm> #include <cstdlib> #include <cstring> #include <ctime> #include <deque> #include <queue> #include <vector> #include <map> using namespace std; const int mod=65521; const int maxn=300; typedef long long LL; int n, m; LL cnt[maxn][maxn]; LL ans, dv; LL gcd(LL b, LL c) { if (c==0) return b; else return gcd(c, b%c); } void init() { for (int i=1; i<n; ++i) for (int j=1; j<n; ++j) { if (i==j) cnt[i][j]=(min(m, n-i)+min(m, i-1))%mod; else if (abs(i-j)<=m) cnt[i][j]=-1; else cnt[i][j]=0; } } void solve() { for (int i=1, j=1; i<n && j<n; ++i, ++j) { int id=i; for (int k=i; k<n; ++k) if (cnt[k][j]!=0) { id=k; break; } if (cnt[id][j]==0) { --i; continue; } if (id!=i) { ans*=-1; for (int k=j; j<n; ++k) swap(cnt[id][k], cnt[i][k]); } for (int k=i+1; k<n; ++k) if (cnt[k][j]!=0) { int tmp1=gcd(abs(cnt[i][j]), abs(cnt[k][j])); int tmp2=cnt[i][j]/tmp1; tmp1=cnt[k][j]/tmp1; dv=dv*tmp2%mod; for (int p=j; p<n; ++p) cnt[k][p]=(cnt[k][p]*tmp2-cnt[i][p]*tmp1)%mod; } } } LL POW(LL b, LL c) { LL s=1, cur=b; while (c) { if (c & 1) s=s*cur%mod; cur=cur*cur%mod; c>>=1; } return s; } int main() { freopen("count.in", "r", stdin); freopen("count.out", "w", stdout); scanf("%d%d", &m, &n); init(); ans=dv=1; solve(); for (int i=1; i<n; ++i) ans=ans*cnt[i][i]%mod; printf("%I64d\n", (ans*POW(dv, mod-2)%mod+mod)%mod); return 0; }