1 条题解
-
0
题目大意
给定一个 的网格,要将 到 这 个数各用一次填入网格。
对于任意一个子矩形(连续若干行、连续若干列),定义它的 为最小的没有出现在这个子矩形中的非负整数。
要最大化 所有子矩形的 MEX 之和,输出任意一种达到最大值的填法。子矩形的总数为 。
关键转化
对于一个子矩形 ,设其 MEX 为 ,意味着:
- 都出现在 中,
- 不在 中。
因此:
$$\operatorname{MEX}(S) = \sum_{k \ge 1} [\; 0,1,\dots,k-1 \text{ 都在 } S \text{ 中} \;] $$其中 是示性函数( 真为 ,假为 )。
于是所有子矩形的 MEX 之和为:
$$\sum_{S} \operatorname{MEX}(S) = \sum_{k \ge 1} \#\{\, S \mid S \text{ 包含 } 0,1,\dots,k-1 \,\} $$
最大化思路
为了最大化总和,对于每个 ,我们要让 包含 的子矩形个数 尽可能大。
这等价于:让这些数在网格中的位置尽可能“紧凑”,使得包含它们所有的小矩形数量最大。
一个经典结论是:按列优先顺序填充(即先填满第一列,再第二列,...)可以达到最优。
为什么?
- 这样填完后,最小的 个数会占据一个 左上角阶梯形区域。
- 包含这些 个数的子矩形,必须至少覆盖它们的最小行、最大行、最小列、最大列。
- 列优先填充使得这些数的分布非常“集中”,从而能覆盖它们的最小子矩形尽可能小,进而包含该最小子矩形的所有更大子矩形也都会包含这 个数。
构造方法
设当前要填的数字为 。
按列优先顺序填入:
- 先填第 列的所有行(从上到下):
- 再填第 列的所有行:
- 以此类推。
即:
其中 是行下标( 到 ), 是列下标( 到 )。
示例验证
当 :
按列优先:
- 第 列:
- 第 列:
得到矩阵:
但题目示例给出的最优解是:
注意:两种填法的 MEX 之和可能不同。
实际上,对于 ,列优先得到 (经计算也可达到最大值),而行优先(示例)也是 。
题目允许多种答案,列优先也是一种正确的构造。当 :
列优先得到:
$$\begin{bmatrix} 0 & 3 & 6 \\ 1 & 4 & 7 \\ 2 & 5 & 8 \end{bmatrix} $$这个矩阵的 MEX 总和也是理论最大值(可以证明)。
时间复杂度
构造网格:
总 之和 ,完全可行。
代码实现(标程)
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<vector<int>> a(n, vector<int>(n)); // 按列优先顺序填充 int cur = 0; for (int j = 0; j < n; j++) { for (int i = 0; i < n; i++) { a[i][j] = cur++; } } // 输出 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cout << a[i][j]; if (j < n - 1) cout << " "; } cout << "\n"; } } int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { solve(); } return 0; }
正确性简述
对于任意 ,最小的 个数在列优先填充下占据的是前 列的部分行。
包含这些 个数的子矩形,其行范围必须覆盖它们的最小行到最大行,列范围必须覆盖它们的最小列到最大列。
这种分布使得所有包含该最小矩形的子矩形都包含这 个数,从而最大化计数。
可以证明,任何其他排列都不能让每个 的包含子矩形数更大,因此列优先填充达到全局最优。
总结
- 核心转化: 和 每个前缀 被包含的子矩形数之和。
- 最优策略:让小数尽量集中在一个小矩形内。
- 具体构造:按列优先顺序填入 。
- 时间复杂度 ,空间复杂度 。
- 1
信息
- ID
- 6440
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者