[luoguP7112] 行列式求值

题意

给定矩阵 \(A\),求 \(A\) 的行列式。

sol

\(n\)\(n\) 列矩阵 \(A\) 的第 \(i\) 行第 \(j\) 列的数为 \(A_{i,j}\),行列式为 \(\det(A)\)\(S_n\)\(1 \sim n\) 的所有全排列,\(\pi(\sigma)\) 为全排列 \(\sigma\) 的逆序对数,则

\[\det(A) = \sum_{\sigma \in S_n} (-1)^{\pi(\sigma)}\prod_{i=1}^n \sigma_i \]

根据定义计算的时间复杂度为 \(O(n!\cdot n)\),无法接受。
为了更快地计算行列式,我们可以得到以下性质:

  1. 当矩阵为上三角矩阵时,行列式为对角线上元素的积;
  2. 交换矩阵的任意两行,矩阵的行列式变为原来的相反数
  3. 将一行的任意多倍加到另一行,行列式不变。

由此,我们发现,操作 \(2,3\) 均属于初等行列变换,因此使用类似于高斯消元的方法,即可将时间复杂度降至 \(O(n^3)\) 量级。
然而与高斯消元不同,我们不希望计算出的行列式存在精度误差,同时被取模数往往不是质数(若是质数可以直接计算逆元)可以使用辗转相减法,不断相减直到某一行为 \(0\)

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
typedef long long LL;

const int N = 605;

int a[N][N];
int n, p;

int det() {
    int w = 1;
    for (int i = 1; i <= n; i ++ ) {
        int k = i;
        for (; k <= n && !a[k][i]; k ++ );
        if (k == n + 1) return 0;
        if (i != k) swap(a[i], a[k]), w = -w;
        for (int j = i + 1; j <= n; j ++ ) {
            while (a[i][i]) {
                int t = a[j][i] / a[i][i];
                for (int k = i; k <= n; k ++ ) 
                    a[j][k] = ((a[j][k] - (LL) t * a[i][k]) % p + p) % p;
                swap(a[i], a[j]);
                w = -w;
            }
            swap(a[i], a[j]), w = -w;
        }
    }
    for (int i = 1; i <= n; i ++ ) w = (LL) w * a[i][i] % p;
    w = (w + p) % p;

    // for (int i = 1; i <= n; i ++ ) {
    //     for (int j = 1; j <= n; j ++ ) printf("%d ", a[i][j]);
    //     puts("");
    // }

    return w;
}

int main(){
    scanf("%d%d", &n, &p);
    for (int i = 1; i <= n; i ++ ) 
        for (int j = 1; j <= n; j ++ )
            scanf("%d", &a[i][j]), a[i][j] %= p;

    printf("%d\n", det());
}
posted @ 2025-04-15 20:41  是一只小蒟蒻呀  阅读(35)  评论(0)    收藏  举报