高斯消元求解行列式

高斯消元

求解

行列式

今天測試的時候出了【NOI2007】生成樹計數,而後完全被提示坑了,用行列式只能作\(40\%\)的數據,正確的解法應該是矩陣乘法,但這個不在文章的討論範圍,本文主要討論如何用高斯消元求解行列式php

首先

要知道什麼是行列式?(百度維基
其實我也不太懂,只知道是\(n \times n\)矩陣的標量。
只要瞭解了行列式的性質,就能夠求值了。測試

一個行列式用:$ \begin{vmatrix} A \end{vmatrix} \(或\)\det(A)$表示,例如url

暫且把\(\det(A)\)的第\(i\)行第\(j\)列的數記做\(a_{ij}\),行列式的值爲spa

\[ \det(A) = \sum_{\sigma \in S_n} sgn(\sigma) \prod_{i=1}^n a_{i,\sigma(i)}\]

\(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至關於每一個全排列的值都乘以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;
}
相關文章
相關標籤/搜索