题解:P13756 【MX-X17-T5】Matrix
咕了半年的题解。
比较简洁的做法。
显然,任何可表示的矩阵 \(A\) 都满足:其每一行与每一列的元素和均为同一个定值 \(S\)。反过来,只要知道 \(A\) 左上角 \((n-1)\times (n-1)\) 的子矩阵以及 \(S\),就可以唯一确定整个矩阵 \(A\)。因此,这类矩阵构成的线性空间维数恰好为 \((n-1)^2+1\)。观察到这恰好等于题目给出的 \(M\) 的上界,因此问题实际上可以转化为,如何恰当地构造出这个矩阵空间的一组基。
一个自然的思路是,在 \(A\) 中选出 \((n-1)^2+1\) 个位置,满足:对于任意一个可表示矩阵 \(B\),如果 \(B\) 在这些位置上的取值都与 \(A\) 相同,那么必有 \(B=A\)。此时,我们只需让基中的每个矩阵对应一个选出的位置,在确定系数时,每个基矩阵负责将自己对应的位置调整到与 \(A\) 相同即可。
考虑如何构造基矩阵,使得我们能较为独立地控制某个位置 \((i,j)\) 上的值。不妨考察一个 \((i,j,n)\) 的置换三元环,也就是令 \(p_i=j,p_j=n,p_n=i\),其他位置上是 \(p_k=k\)。这样构造的主要动机是,我们更加关心左上角 \((n-1)\times (n-1)\) 的子矩阵的取值,可以把第 \(n\) 行和第 \(n\) 列视作一个缓冲区。然而在 \(i=j\) 时上述构造不成立,此时不妨直接考虑置换二元环 \((i,n)\),令 \(p_i=n,p_n=i\),但是这样就控制不了 \((i,i)\) 的取值了。这启发我们这样选位置:取左上角 \((n-1)\times (n-1)\) 的子矩阵中非对角线的位置,再把第 \(n\) 列全部选上。对三类位置分别构造基矩阵:
- \(1\leq i,j<n\land i\neq j\):取三元环,令 \(p_i=j,p_j=n,p_n=i\)。
- \(1\leq i<n,j=n\):取二元环,令 \(p_i=n,p_n=i\)。
- \(i=j=n\):取单位矩阵。
这样我们就得到了基矩阵的构造。
记控制位置 \((i,j)\) 的基矩阵为 \(B_{i,j}\),我们来证明我们构造的是一组合法的基。
证明
要证明我们构造的基矩阵线性无关,等价于证明 \(\sum c_{i,j}B_{i,j}\) 的唯一解是 \(c_{i,j}\) 都为 \(0\)。
还是对三类位置分类讨论:
- \(1\leq i,j<n\land i\neq j\):只有 \(B_{i,j}\) 能在 \((i,j)\) 处贡献 \(1\),因此 \(c_{i,j}=0\)。
- \(1\leq i<n,j=n\):由于第一类位置的系数恒为 \(0\),只有 \(B_{i,n}\) 能在 \((i,n)\) 处贡献 \(1\),因此 \(c_{i,n}=0\)。
- \(i=j=n\):只有单位矩阵能在 \((n,n)\) 处贡献 \(1\),因此 \(c_{n,n}=0\)。
综上,所有 \(c_{i,j}\) 都为 \(0\)。\(\Box\)
系数的构造也呼之欲出了:
- \(i=j=n\):\(c_{n,n}=A_{n,n}\)。
- \(1\leq i,j<n\land i\neq j\):\(c_{i,j}=A_{i,j}\)。
- \(1\leq i<n,j=n\):注意到形如 \((k,i,n)\) 的置换三元环也会对 \((i,n)\) 产生贡献,需要减掉,即 \(c_{i,n}=A_{i,n}-\sum\limits_{k=1}^{n-1}[k\neq i]A_{k,i}\)。
直接按照上述过程构造即可。时间复杂度为 \(\mathcal{O}(n^3+qn^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 201;
template<typename T> inline T lowbit(T x) { return x & -x; }
template<typename T> inline void chk_min(T &x, T y) { x = y < x ? y : x; }
template<typename T> inline void chk_max(T &x, T y) { x = x < y ? y : x; }
int n, q, m, p[MAXN], A[MAXN][MAXN];
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q >> m;
m = (n - 1) * (n - 1) + 1;
cout << m << '\n';
iota(p + 1, p + n + 1, 1);
for (int k = 1; k <= n; ++k) cout << p[k] << " \n"[k == n];
for (int i = 1; i < n; ++i) for (int j = 1; j < n; ++j) {
p[i] = j, p[j] = n, p[n] = i;
for (int k = 1; k <= n; ++k) cout << p[k] << " \n"[k == n];
p[i] = i, p[j] = j, p[n] = n;
}
while (q--) {
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) cin >> A[i][j];
ll S = accumulate(A[1] + 1, A[1] + n + 1, 0ll);
bool ok = 1;
for (int i = 2; i <= n; ++i)
if (accumulate(A[i] + 1, A[i] + n + 1, 0ll) != S) { ok = 0; break; }
for (int j = 1; j <= n; ++j) {
ll sum = 0;
for (int i = 1; i <= n; ++i) sum += A[i][j];
if (sum != S) { ok = 0; break; }
}
if (!ok) { cout << "0\n"; continue; }
cout << "1 ";
cout << A[n][n] << ' ';
for (int i = 1; i < n; ++i) for (int j = 1; j < n; ++j) {
if (i != j) cout << A[i][j] << ' ';
else {
ll c = A[i][n];
for (int k = 1; k < n; ++k) if (k != i) c -= A[k][i];
cout << c << ' ';
}
}
cout << '\n';
}
return 0;
}

浙公网安备 33010602011771号