[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)\),无法接受。
为了更快地计算行列式,我们可以得到以下性质:
- 当矩阵为上三角矩阵时,行列式为对角线上元素的积;
- 交换矩阵的任意两行,矩阵的行列式变为原来的相反数
- 将一行的任意多倍加到另一行,行列式不变。
由此,我们发现,操作 \(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());
}

浙公网安备 33010602011771号