1 条题解

  • 0
    @ 2026-5-3 14:15:03

    Trulicina 的网格构造 详细题解

    问题重述

    给定三个整数 n,m,kn, m, k,满足 k2k \ge 2nm0(modk)n \cdot m \equiv 0 \pmod k。要求构造一个 nnmm 列的网格,每个格子填入 1k1 \sim k 的整数,并满足:

    1. 每个整数 1k1 \sim k 在网格中出现的总次数相同(均为 nmk\frac{n\cdot m}{k} 次);
    2. 任意两个相邻(共享一条边)的格子中的整数不同。

    题目保证这样的网格总是存在。

    直观尝试:自然阅读顺序填充

    一个直接的想法是,按照从上到下、从左到右的顺序,循环填入 1,2,,k,1,2,1, 2, \dots, k, 1, 2, \dots。即第 ii 行第 jj 列(00‑based 索引,i=0,,n1i=0,\dots,n-1j=0,,m1j=0,\dots,m-1)的数为:

    ai,j=((im+j)modk)+1.a_{i,j} = ((i \cdot m + j) \bmod k) + 1.

    例如,当 n=3,m=4,k=6n=3, m=4, k=6 时,得到:

    1 2 3 4 5 6 1 2 3 4 5 6

    该方案自动满足“每个数出现次数相等”,因为总共 nmn\cdot mkk 的倍数,循环填数恰好使每个 1k1\sim k 都出现 nmk\frac{n\cdot m}{k} 次。

    该方案何时失败?

    检查相邻格子的差(模 kk 意义下):

    • 水平相邻ai,j+1ai,j1(modk)a_{i,j+1} - a_{i,j} \equiv 1 \pmod k(因为加 11)。只要 k2k \ge 21≢0(modk)1 \not\equiv 0 \pmod k,所以水平方向永远不会冲突。
    • 垂直相邻ai+1,jai,jm(modk)a_{i+1,j} - a_{i,j} \equiv m \pmod k(因为跨过一整行,索引增加 mm)。因此,当 m≢0(modk)m \not\equiv 0 \pmod k 时,垂直差不为 00,相邻不同;但当 mmkk 的倍数时m0(modk)m \equiv 0 \pmod k,导致 ai+1,j=ai,ja_{i+1,j} = a_{i,j},垂直相邻格子数字相同,违反条件。

    例如 n=4,m=6,k=3n=4, m=6, k=3mmkk 的倍数,自然顺序填入得到:

    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3

    垂直相邻全是相同的数字,不符合要求。

    修正方案:对部分行进行循环移位

    mmkk 的倍数时,我们需要通过“错位”来打破垂直相邻的相同。一个通用的构造是:仍然遵循逐行循环填充 1k1\sim k,但当检测到当前行与上一行在某一列出现相同数字时,将整行向左循环移动一个位置

    在实现中,我们逐行生成。对于第 ii 行,先按自然顺序生成一个临时行:

    tmpj=((im+j)modk)+1.tmp_j = ((i \cdot m + j) \bmod k) + 1.

    然后检查该行是否与上一行(存储在 LAST 中)有任何同列相同。若有,则将临时行循环左移一位,即新行 CURj=tmp(j+1)modmCUR_j = tmp_{(j+1)\bmod m}。再将 CUR 作为当前行输出,并把它更新为新的 LAST

    为什么一次循环移位就能修正?

    mmkk 的倍数,且第 i1i-1 行的第 jj 列数字为 xx。自然顺序下,第 ii 行同列的数字也是 xx(因为 m0(modk)m \equiv 0 \pmod k)。进行循环左移一位后,原来位置 jj 的元素被原来位置 j+1j+1 的元素取代,即当前行 jj 列的数字变为 ((im+j+1)modk)+1((i \cdot m + j + 1) \bmod k) + 1

    计算垂直差(模 kk):

    $$\begin{aligned} CUR_j - LAST_j &\equiv \big( (i\cdot m + j + 1) - ((i-1)\cdot m + j) \big) \pmod k \\ &\equiv m + 1 \pmod k. \end{aligned} $$

    由于 mmkk 的倍数,m0(modk)m \equiv 0 \pmod k,故差为 1(modk)1 \pmod k,不为 00。因此移位后,所有垂直相邻对均不同。

    同时,移位操作保持水平相邻差仍为 11(或 k1k-1)。因为移一位后,整行仍是形如 [s,s+1,,k,1,,s1][s, s+1, \dots, k, 1, \dots, s-1] 的循环连续段,相邻位置仍差 1(modk)1 \pmod k,不会相同。

    图示示例

    仍用 n=4,m=6,k=3n=4, m=6, k=3

    • 第 1 行(i=0i=0):自然顺序 1 2 3 1 2 3,与上一行(空)不冲突,不移位。
    • 第 2 行(i=1i=1):自然顺序 1 2 3 1 2 3,与上一行完全相同,触发移位。左移一位后变成 2 3 1 2 3 1
    • 第 3 行(i=2i=2):自然顺序 1 2 3 1 2 3,与上一行 2 3 1 2 3 1 比较,有同列相同吗?第 2 行第 2 列是 3,第 3 行自然顺序同列也是 3,所以移位。左移一位后变成 2 3 1 2 3 1 吗?但代码会继续判断。实际上标准程序在此场景下会交替出现 1 2 3 1 2 32 3 1 2 3 1,最终输出正与题面示例一致:

    1 2 3 1 2 3 2 3 1 2 3 1 1 2 3 1 2 3 2 3 1 2 3 1

    垂直相邻差为 1(modk)1 \pmod k,水平差也为 1(modk)1 \pmod k,完美符合条件。

    mm 不是 kk 的倍数时

    此时自然顺序已经满足条件,shift 始终为 false,不会移位。输出即为自然顺序填充。

    特判:m=1m = 1

    m=1m = 1 时,网格只有一列,不存在水平相邻限制。唯一要求是垂直相邻不同且每个数出现次数相等。我们可以简单地让第 ii 行填 (imodk)+1(i \bmod k) + 1,这样相邻行差为 1(modk)1 \pmod k,且每个数字恰好出现 nk\frac{n}{k} 次(因为 nnkk 的倍数)。

    标准程序中对 m=1m=1 做了特殊处理,直接输出,不进入后续逻辑。

    算法流程

    1. 读入 tt
    2. 对每个测试用例:
    • 读入 n,m,kn, m, k
    • m=1m = 1
      • 对于 i=0n1i = 0 \dots n-1,输出 (imodk)+1(i \bmod k) + 1,并换行。
      • 继续下一个用例。
    • 初始化数组 LAST 长度为 mm,值全为 1-1(表示上一行,初始无限制)。
    • 对于 i=0n1i = 0 \dots n-1
      • 创建当前行数组 CUR 长度为 mm
      • shift = false
      • 对于 j=0m1j = 0 \dots m-1
        • elm = ((i * m + j) % k) + 1
        • CUR[j] = elm
        • elm == LAST[j],则 shift = true
      • shift 为真:
        • CUR 循环左移一位:newCUR[j] = CUR[(j + 1) % m]
        • newCUR 替换 CUR
      • 输出 CURmm 个整数,空格分隔,行末换行。
      • LAST = CUR(更新上一行)。

    正确性证明

    每个数字出现次数相等

    无论是自然顺序还是经过移位,每一行都是 1k1 \sim k 的某个循环排列的重复。具体来说,若一行有 mm 个元素,且 kk 整除 mm(因为我们只在 mmkk 的倍数时才移位,而 mm 不是 kk 的倍数时不移位),那么这一行恰好包含完整的 mk\frac{m}{k}1k1 \sim k。当 mm 不是 kk 的倍数时,整行仍由若干个完整周期和一个部分周期组成,但整个网格总元素数 nmn\cdot mkk 的倍数,且每行的填充方式都是连续的块,可以证明每种数字出现的总次数为 nmk\frac{n\cdot m}{k}

    更严谨的论证:无论是否移位,第 ii 行第 jj 列的数字总可以写成 ((im+j+δi)modk)+1((i\cdot m + j + \delta_i) \bmod k) + 1 的形式,其中 δi{0,1}\delta_i \in \{0, -1\}(不移位时 δi=0\delta_i = 0,移位时相当于 δi=1\delta_i = -1)。整个网格的数字序列是将 0nm10 \sim n\cdot m - 1 的连续整数加上每行的偏移后对 kk 取模。由于每行的偏移都是常数,且 nmn\cdot mkk 的倍数,容易得知每个余数出现的次数完全相等。

    相邻格子不同

    • 水平相邻:不移位时差为 11;移位后由于整行循环左移,相邻元素在模意义下仍相差 11(原来是连续的数,循环移位后依然是连续的数)。因此水平相邻永不相同。
    • 垂直相邻
    • m≢0(modk)m \not\equiv 0 \pmod k,不移位,差为 m≢0m \not\equiv 0,不同。
    • m0(modk)m \equiv 0 \pmod k,当不移位时垂直差为 00,但此时必然有同列相同导致 shift = true,因此一定会移位。移位后垂直差变为 m+11(modk)m + 1 \equiv 1 \pmod k,不同。

    因此所有相邻对均不相同。

    复杂度分析

    对于每个测试用例,我们逐行逐列生成网格,时间复杂度为 O(nm)O(n \cdot m)。由于所有测试用例的 nmn \cdot m 之和不超过 2×1052 \times 10^5,总时间完全在限制内。空间复杂度同样为 O(m)O(m),只需存储当前行和上一行。

    总结

    本题通过简单的“自然顺序+条件移位”构造,巧妙解决了当列数是 kk 的倍数时垂直相邻冲突的问题。关键在于分析垂直相邻差 mmodkm \bmod k,并通过一行循环移位将差调整为 1modk1 \bmod k,同时不影响水平相邻的性质。这种构造方法简洁且易于实现,是 CF 中典型的有趣构造题。

    标准程序

    #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, m, k;
            cin >> n >> m >> k;
            // 特判 m == 1 的情况:行内没有相邻限制,只需行间不同且每个数出现相等
            if (m == 1) {
                for (int i = 0; i < n; ++i) {
                    cout << (i % k) + 1 << '\n';
                }
                continue;
            }
            vector<int> LAST(m, -1);
            for (int i = 0; i < n; ++i) {
                vector<int> CUR(m);
                bool shift = false;
                for (int j = 0; j < m; ++j) {
                    int elm = ((i * m + j) % k) + 1;
                    CUR[j] = elm;
                    if (elm == LAST[j]) {
                        shift = true;
                    }
                }
                if (shift) {
                    // 循环左移一位(对应 Python 的 CUR[(j+1)%m])
                    vector<int> newCUR(m);
                    for (int j = 0; j < m; ++j) {
                        newCUR[j] = CUR[(j + 1) % m];
                    }
                    CUR = move(newCUR);
                }
                for (int j = 0; j < m; ++j) {
                    cout << CUR[j] << " \n"[j == m - 1];
                }
                LAST = CUR;
            }
        }
        return 0;
    }
    
    • 1

    信息

    ID
    6743
    时间
    2000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    2
    已通过
    1
    上传者