Skip to content

喵~ 主人好呀!这次我们来解决一道非常有趣的题目:B. Corner Twist。这道题就像一个数字魔术,我们要揭开它背后的秘密,哼哼,看我的厉害!

题目大意

这道题是这样的,喵~

我们有两个大小都是 n×mn \times m 的网格,分别叫做 aabb。格子里面的数字只有 0,1,20, 1, 2 这三种,就像猫咪的三种心情一样!

我们可以对网格 aa 进行一种特殊的操作,而且想做多少次都行:

  1. 在网格里选一个长和宽都至少为 2 的矩形区域。
  2. 这个矩形有四个角角。我们选其中一对对角线上的角,把它们的值都加上 1。
  3. 剩下没被选中的另一对对角线上的角,就把它们的值都加上 2。

所有的加法都是在模 3 的意义下进行的哦!也就是说,一个数加了之后如果变成了 3,它就会变回 0;如果是 4,就会变成 1。

问题就是:我们能不能通过这些操作,把网格 aa 变得和网格 bb 一模一样呢?如果可以,就告诉人家 "YES",不行就说 "NO",很简单吧,喵~

解题方法

直接一步步模拟操作肯定是不行的啦,太慢了,会超时的说。这种时候,我们就要开动小脑筋,寻找操作中隐藏的规律,也就是所谓的 “不变量”

让我们仔细分析一下这个神奇的操作,喵。

假设我们选择了一个左上角为 (r1, c1),右下角为 (r2, c2) 的矩形。操作会改变四个角的值:a[r1][c1], a[r1][c2], a[r2][c1], a[r2][c2]

有两种可能的变化情况(所有计算都在模 3 意义下):

  1. a[r1][c1]a[r2][c2] 加 1,同时 a[r1][c2]a[r2][c1] 加 2。
  2. a[r1][c1]a[r2][c2] 加 2,同时 a[r1][c2]a[r2][c1] 加 1。

现在,让我们来观察一下每一和每一的总和在操作后会发生什么变化。这可是解题的关键所在哦!

对行的影响

对于任意一行 i

  • 如果这一行 i 不在矩形的上下边界上(即 r1 < i < r2),那么它完全不受影响,总和当然不变。
  • 如果这一行 i 正好是矩形的上边界(i = r1),那么 a[r1][c1]a[r1][c2] 的值会被改变。它们增加的值,无论是 +1+2,还是 +2+1,加起来都是 1 + 2 = 3。在模 3 的世界里,3 就等于 0!所以,这一行的总和在模 3 意义下,其实没有改变
  • 同理,如果这一行 i 是下边界(i = r2),其总和的变化也是 1 + 2 = 3,模 3 后还是 0

结论一: 无论我们怎么操作,任何一行的所有元素之和,在模 3 意义下都是一个不变量

对列的影响

  • 和行的分析一模一样,喵~ 如果某一列 j 处在矩形的左右边界上,那么它上面两个被影响的元素值的总变化量也是 1 + 2 = 3,模 3 之后等于 0

结论二: 无论我们怎么操作,任何一列的所有元素之和,在模 3 意义下也是一个不变量

找到这两个不变量,问题就迎刃而解啦!

如果网格 aa 可以变成网格 bb,那么它们必须始终遵守这两个不变的性质。也就是说,对于最终状态 bb 和初始状态 aa,必须满足:

  1. 对于任意一行 ia 的第 i 行元素之和 mod 3 必须等于 b 的第 i 行元素之和 mod 3
  2. 对于任意一列 ja 的第 j 列元素之和 mod 3 必须等于 b 的第 j 列元素之和 mod 3

只要检查这两个条件就好啦!如果 aabb 都满足这两个条件,我们就可以认为 aa 能变成 bb。反之,只要有任何一行或一列不满足,就绝对不可能变过去,直接回答 "NO" 就好啦,喵~

题解

下面就是代码实现啦,主人请看~

cpp
#include <iostream>
#include <vector>
#include <string>
#include <numeric>

void solve() {
    int n, m;
    std::cin >> n >> m;

    // 用 vector 来存网格,就像猫咪的小肉垫一样柔软好用~
    std::vector<std::vector<int>> a(n, std::vector<int>(m));
    for (int i = 0; i < n; ++i) {
        std::string row_str;
        std::cin >> row_str;
        for (int j = 0; j < m; ++j) {
            a[i][j] = row_str[j] - '0'; // 把字符'0','1','2'变成数字0,1,2
        }
    }

    std::vector<std::vector<int>> b(n, std::vector<int>(m));
    for (int i = 0; i < n; ++i) {
        std::string row_str;
        std::cin >> row_str;
        for (int j = 0; j < m; ++j) {
            b[i][j] = row_str[j] - '0';
        }
    }

    // 检查每一行,喵~
    for (int i = 0; i < n; ++i) {
        int row_sum_a = 0;
        int row_sum_b = 0;
        for (int j = 0; j < m; ++j) {
            row_sum_a += a[i][j];
            row_sum_b += b[i][j];
        }
        // 如果 a 和 b 对应行的和模 3 后不相等,那肯定不行啦!
        if (row_sum_a % 3 != row_sum_b % 3) {
            std::cout << "NO\n";
            return; // 直接结束,告诉主人不行
        }
    }

    // 检查每一列,喵~
    for (int j = 0; j < m; ++j) {
        int col_sum_a = 0;
        int col_sum_b = 0;
        for (int i = 0; i < n; ++i) {
            col_sum_a += a[i][j];
            col_sum_b += b[i][j];
        }
        // 如果 a 和 b 对应列的和模 3 后不相等,也肯定不行!
        if (col_sum_a % 3 != col_sum_b % 3) {
            std::cout << "NO\n";
            return; // 同样直接结束
        }
    }

    // 所有检查都通过了,说明 a 可以变成 b,太棒啦!
    std::cout << "YES\n";
}

int main() {
    // 加速输入输出,让程序跑得像猫一样快!
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);
    int t;
    std::cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

知识点介绍

主人,做完题也要学习新知识才能进步呀,就像猫咪每天都要磨爪子一样,喵~

1. 模运算 (Modular Arithmetic)

这道题的核心就是模 3 运算。模运算就像一个只有 0, 1, 2 三个数字的时钟。2+1=3,在时钟上转了一圈又回到了 0,所以 3 % 3 = 02+2=4,转了一圈还多一步,就到了 1,所以 4 % 3 = 1。 在题目里,对角线上的数字分别加 1 和 2,总和的变化是 1+2=3。在模 3 的世界里,30 是等价的。正是这个性质,才让我们找到了解题的突破口呢!

2. 不变量 (Invariants)

不变量是算法竞赛中一个超级重要的概念!它指的是在进行一系列操作的过程中,某个属性或数值始终保持不变。就像不管猫咪怎么打滚,它还是那只可爱的猫咪一样,喵~ 在这道题里,我们发现**“任意一行的元素和模 3 的值”“任意一列的元素和模 3 的值”**就是两个不变量。无论我们进行多少次题目中定义的操作,这两个值都不会改变。

寻找不变量是一种非常强大的解题策略。当题目问“是否可能”时,可以先思考:如果可能,那么初始状态和目标状态之间必须有什么共同点?这个共同点往往就是不变量。如果连这个共同点都不满足,那答案肯定是“不可能”啦。

3. 必要条件与充分条件

我们找到的这个“行列和模 3 相等”的性质,是一个必要条件。也就是说,要想 aa 能变成 bb必须满足这个条件。而这道题比较特殊,这个必要条件恰好也是充分条件(虽然我们没有严格证明这一点,但在竞赛中通常可以相信题目的设计)。充分条件的意思是,只要满足了这个条件,就一定能从 aa 变成 bb

所以,我们只需要检查这个条件,就能得到正确的答案。这大大简化了问题,不用去思考具体怎么操作啦,是不是很巧妙呢?

好啦,这次的题解就到这里啦!希望对主人有帮助,喵~ 下次再一起玩耍吧!

Released under the MIT License.