1 条题解
-
0
题目翻译
E. 构造矩阵
给定一个偶数 和一个整数 ,构造一个 的 矩阵,满足:- 矩阵中所有数的和恰好为 ;
- 每一行的 XOR 值相同;
- 每一列的 XOR 值相同。
若可能,输出
Yes及矩阵;否则输出No。
一、基本性质分析
1.1 XOR 与奇偶性
对于 序列,XOR 等于 当且仅当 的个数为奇数。
因此,“行 XOR 相同” 等价于 每行 的个数的奇偶性相同。列同理。设每行有 个 ( 为整数,奇偶性固定),则总 数 。
因为 是偶数,所以: [ k \equiv 0 \pmod{2} ] 结论 1: 必须是偶数。若 为奇数,直接输出No。1.2 特殊情况 或
-
若 ,只有两个 。
由于每行 XOR 相等,这两个 必须在同一行(否则两行 XOR 不同)。
但这样就会有两列各有一个 ,其余列全 ,列 XOR 不相等。
当 时,可以构造: [ \begin{bmatrix}1 & 1 \ 0 & 0\end{bmatrix} ] 行 XOR:;列 XOR:,满足要求。
对于 ,无解。 -
若 ,相当于全 矩阵去掉两个 。
对称分析可知,也只有 时可行(此时 ,同上)。
结论 2:若 且 或 ,则无解。
二、一般情况的构造
当 为偶数且不满足上述特殊情况时,一定有解。
我们按 分类构造。2.1 情况一:
令 , 为非负整数。
构造方法:
将 矩阵划分为 个互不重叠的 子矩阵。
每个 子矩阵全填 会贡献 个 ,且该子矩阵的每行有 个 (偶数),每列有 个 (偶数),因此不会破坏整体行/列 XOR 的相等性(保持为 )。我们只需选择 个这样的子矩阵(例如按行优先顺序取前 个),将它们全部填 ,其余位置填 。
验证:
- 总 数 。
- 每行包含若干个完整的 块,每个块在该行贡献 个 ,因此每行 的个数为偶数 → 行 XOR 均为 。
- 同理每列 XOR 也为 。
2.2 情况二:
此时 ,且 (因为 且 已排除)。
第一步:放置一个 的“种子”模式
在左上角 子矩阵中填入以下 个 : [ \begin{bmatrix} 1 & 1 & 0 \ 1 & 0 & 1 \ 0 & 1 & 1 \end{bmatrix} ] 即位置:。
检查:每行每列均有 个 (偶数),因此行/列 XOR 均为 。
第二步:填充剩余的 个
因为 ,所以 ,设 。
固定前 行 列,其中前 行 列已填好种子模式,第 行和第 列暂时全 。
剩下的部分是 的子矩阵,其行数和列数均为偶数(因为 偶, 偶)。
我们可以将这个子矩阵划分为 个 块,每个块可以独立地全填 。我们只需从中选择 个 块全填 ,其余全 。
第三步:处理边界情况
当 时,,需要 个 全 块。
但 区域最多只能容纳 个 块,当 时这个数小于 。因此需要特殊处理:在 的左上角额外再填 个 ,位置为 。
此时 区域变为: [ \begin{bmatrix} 1 & 1 & 1 & 1 \ 1 & 0 & 1 & 0 \ 0 & 1 & 1 & 0 \ 0 & 0 & 1 & 1 \end{bmatrix} ] 每行每列均为偶数个 ,共 个 。
此时 ,恰好等于 区域全部填满 全 块的总 数。
三、算法步骤总结
对于每个测试用例:
- 若 为奇数 → 输出
No。 - 若 且 ( 或 ) → 输出
No。 - 否则:
- 初始化全 矩阵。
- 若 : 令 ,按行优先顺序取 个不重叠的 子矩阵全填 。
- 若 : 在 填 。 若 ,额外在 填 。 令 (若 则 ),从第 行第 列开始填充 个 全 块。
- 输出
Yes和矩阵。
四、参考代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, k; cin >> n >> k; // 无解判断 if (k % 2 == 1) { cout << "No\n"; continue; } if (n > 2 && (k == 2 || k == n * n - 2)) { cout << "No\n"; continue; } // 有解,开始构造 cout << "Yes\n"; vector<vector<int>> a(n, vector<int>(n, 0)); if (k % 4 == 0) { // k ≡ 0 (mod 4):填充 2x2 全1块 int cnt = k / 4; for (int i = 0; i + 1 < n && cnt > 0; i += 2) { for (int j = 0; j + 1 < n && cnt > 0; j += 2) { a[i][j] = a[i][j+1] = a[i+1][j] = a[i+1][j+1] = 1; cnt--; } } } else { // k ≡ 2 (mod 4):种子模式 a[0][0] = a[0][1] = 1; a[1][0] = a[1][2] = 1; a[2][1] = a[2][2] = 1; // 处理 k = n^2 - 6 的特殊情况 if (k == n * n - 6) { a[0][2] = a[0][3] = 1; a[3][2] = a[3][3] = 1; } // 填充剩余的 2x2 全1块 int remaining = k - 6; if (k == n * n - 6) remaining -= 4; int cnt = remaining / 4; for (int i = 4; i + 1 < n && cnt > 0; i += 2) { for (int j = 4; j + 1 < n && cnt > 0; j += 2) { a[i][j] = a[i][j+1] = a[i+1][j] = a[i+1][j+1] = 1; cnt--; } } } // 输出矩阵 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cout << a[i][j] << " \n"[j == n-1]; } } } return 0; }
五、复杂度分析
- 时间复杂度: 每个测试用例
- 空间复杂度:
- 所有测试用例的 之和不超过 ,完全可行
六、示例验证
输入 输出 Yes+ 全 矩阵Yes+ 种子模式扩展到NoYes+ 全 矩阵
- 1
信息
- ID
- 6248
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者