2021icpc 济南 J Determinant(高斯消元 + 行列式求值)

2021icpc 济南 J Determinant(高斯消元 + 行列式求值)


题目来源:J Determinant

购买的时候将时光机取消即可


题意:

给出 n * n 的行列式的值的绝对值 det 以及这个行列式, 判断该行列式的值的正负, 其中 det 最多有1e4的位数


思路:

  • 因为线性代数太久没看过了, 比赛的时候甚至都不知道 det 是行列式的值,还是一点一点推出来的, 发现 A的逆 乘 A 事实上就是A的第 i 列和第 j 列相乘, 所以化成上三角之后有用的部分只有主对角线的部分, 那么第 i 列和第 i 列相乘就是 A[i][i] * A[i][i], 那么最后就是相当于去找 行列式的值 的正负.

  • 但是这个题中 det 最多有 1e4 的位数,所以肯定不能直接用高斯消元直接将行列式化成上三角的形式, 同时也不能只是用 double, 最后判正负号的个数, 因为有的地方也会爆 double.

  • 我们需要取一个比较大的素数, 每次以取模的形式将行列式化成上三角的形式, 最后用行列式的值与 det % mod 的值去比较

    • 若 det % mod == ans, 说明 det 是正的
    • 否则, det % mod + ans 一定等于 n, det是负的, 比如 (-7 % 5 + 5) % 5 = 3, 7 % 5 = 2
  • 需要注意的是, 使用高斯消元交换行列式的两行时, 行列式的正负会改变


AC代码

#include <bits/stdc++.h>
#define endl "\n"
#define rep(i, m, n) for (int i = (m); i <= (n); ++i)
#define rrep(i, m, n) for (int i = (m); i >= (n); --i)
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 110, mod = 1e9 + 7;
ll n, p[N][N], det;
string s;
ll fact() { // 将 字符串s 转化成 整形det
	ll m = 0, l = s.length();
	rep(i, 0, l - 1) {
		m = m * 10 + s[i] - '0';
		m %= mod;
	}
	return m;
}
ll qpow(int a, int b) {
	ll res = 1; a %= mod;
	while (b) {
		if (b & 1) res = (ll)res * a % mod;
		a = (ll)a * a % mod; b >>= 1;
	}
	return res;
}
ll inv(ll x) { return qpow(x, mod - 2); } // 逆元
ll guess() { // 高斯消元 求 行列式
	ll ans = 1;
	rep(i, 1, n) {
		rep(j, i, n)
			if (p[j][i]) { // 不能让 p[i][i] = 0, 即对角线的部分不能为0
				rep(k, i, n) swap(p[i][k], p[j][k]);
				if (i != j) ans = -ans;//交换两行,行列式正负改变
				break;
			}

		// 用第 i 行去修改第 j 行
		// p[j][k] = p[j][k] - p[i][k] * p[j][i] / p[i][i];
		for (int j = i + 1, invf = inv(p[i][i]); j <= n; ++j) {
			ll t = p[j][i] * invf % mod;
			rrep(k, n, i) p[j][k] = ((p[j][k] - p[i][k] * t % mod) % mod + mod) % mod;
		}

		// 行列式的值就是化成上三角后主对角线的积乘上已经提取出来的数字
		ans = (ans * p[i][i] % mod + mod) % mod;
	}

	return ans;
}
void solve() {
	scanf("%lld", &n);
	cin >> s; det = fact();
	rep(i, 1, n) rep(j, 1, n)
		scanf("%lld", &p[i][j]);

	ll res = guess();
	if (res == det) puts("+");
	else puts("-");
}
int main() {
	int t; cin >> t;
	while (t--) solve();
	return 0;
}

END

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值