LOJ #3409. 「2020-2021 集训队作业」Yet Another Linear Algebra Problem 题解

Description

您需要解决两个独立(但类似)的子问题:

问题一:给定 \(n\) 个在 \(\mathrm{GF}(3)\) 上的 \(m\) 维向量,记它们张成的线性空间为 \(V\)。求从 \(n\) 个向量中选出一组向量,使得它们是 \(V\) 的基的方案数。对 \(3\) 取模。

问题二:给定 \(n\) 个在 \(\mathrm{GF}(2)\) 上的 \(m\) 维向量,记它们张成的线性空间为 \(V\)。其中第 \(i\) 个向量有颜色 \(c_i\)。求从每种颜色中恰好选出一个向量,使得它们是 \(V\) 的基的方案数。对 \(2\) 取模。

注:为了凸显主要矛盾而忽略次要矛盾,保证 \(V\) 的维数为 \(m\)

\(1\leq n,m\leq 500\)

Solution

首先有个前置知识是 Binet Cauchy 公式:

对于 \(m\times n\) 的矩阵 \(A\)\(n\times m\) 的矩阵 \(B\)\(n\geq m\)),有:(\(A_S,B_S\) 表示列/行下标在 \(S\) 中的子矩阵)

\[\det(AB)=\sum_{|S|=m}{\det(A_S)\det(B_S)} \]

第一问就是问有多少个大小为 \(m\) 的子集,使得这个子集的秩非零。注意到在模 \(3\) 意义下,\([x\neq 0]=x^2\),所以问题变为 \(\displaystyle\sum_{|S|=m}{\det(A_S)^2}=\det(A^TA)\)

第二问等价于问 \(\displaystyle\sum_{|S|=m}{\det(A_S)[\cup_{i\in S}{\{c_i\}}=U]}\),其中 \(u\) 是颜色全集。考虑将第二个条件转为一个 \(n\times m\) 的矩阵的行列式。

容易发现这等价于让 \(B_{i,j}=[j=c_i]\),如果 \(S\) 中有元素颜色相同,则 \(B_S\) 的秩一定是 \(0\),否则是 \(1\)。于是答案为 \(\det(A^TB)\)

时间复杂度:\(O(n^3)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 505;

int taskid, n, m, mod;

struct Matrix {
  int n, m, a[kMaxN][kMaxN];
  void set(int _n, int _m) { n = _n, m = _m; }
  friend Matrix operator *(Matrix &a, Matrix &b) {
    static Matrix ret;
    assert(a.m == b.n);
    ret.set(a.n, b.m);
    for (int i = 1; i <= a.n; ++i) {
      for (int j = 1; j <= b.m; ++j) {
        ret.a[i][j] = 0;
        for (int k = 1; k <= a.m; ++k) ret.a[i][j] += a.a[i][k] * b.a[k][j];
        ret.a[i][j] %= mod;
      }
    }
    return ret;
  }
} a, b;

int getdet(Matrix a) {
  assert(a.n == a.m);
  int n = a.n, ret = 1;
  for (int i = 1; i <= n; ++i) {
    if (!a.a[i][i]) {
      for (int j = i + 1; j <= n; ++j) {
        if (a.a[j][i]) {
          std::swap(a.a[i], a.a[j]), ret = mod - ret;
          break;
        }
      }
    }
    if (!a.a[i][i]) return 0;
    ret = ret * a.a[i][i] % mod;
    for (int j = i + 1; j <= n; ++j) {
      int d = a.a[j][i] * a.a[i][i];
      for (int k = i; k <= n; ++k) a.a[j][k] = (a.a[j][k] - a.a[i][k] * d + 3 * mod) % mod;
    }
  }
  return (ret % mod + mod) % mod;
}

void dickdreamer() {
  std::cin >> taskid >> n >> m;
  if (taskid == 1) {
    mod = 3, a.set(m, n), b.set(n, m);
    for (int i = 1; i <= n; ++i)
      for (int j = 1; j <= m; ++j)
        std::cin >> b.a[i][j], a.a[j][i] = b.a[i][j];
    std::cout << getdet(a * b) << '\n';
  } else {
    mod = 2, a.set(m, n), b.set(n, m);
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j)
        std::cin >> a.a[j][i];
      int c;
      std::cin >> c;
      b.a[i][c] = 1;
    }
    std::cout << getdet(a * b) << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-08-27 09:39  下蛋爷  阅读(14)  评论(0)    收藏  举报