错综的统一

错综的统一

题目描述

小红拿到了一个仅由 'r'、'e'、'd' 三种字母组成的矩阵,共 $n$ 行 $m$ 列。

小红定义一个矩形区域是美丽的,当且仅当:在该矩形区域中,任意横向或者纵向取一个长度大于1的连续子串时,该字符串都不是回文的。

现在小红有若干次询问,每次给定一个矩形区域,问最少修改多少字符(字符只能修改为 'r'、'e'、'd'),可以使得该区域为“美丽的”?

输入描述:

第一行输入三个正整数 $n,m,q$ ,代表矩阵的行和列,以及询问次数。

接下来的 $n$ 行,每行输入一个长度为 $m$ 的、只含有 'r'、'e'、'd' 三种字母字符串,用来表示字符矩阵。

接下来的 $q$ 行,每行输入四个正整数 $x_1,y_1,x_2,y_2$ ,代表每次询问的矩形区域的左上角坐标 $(x_1,y_1)$ 和右下角坐标 $(x_2,y_2)$。

数据范围:

$1\leq n ,m \leq 500, 1\leq q \leq 100000$

$1\leq x_1\leq x_2\leq n,1\leq y_1\leq y_2\leq m$

输出描述:

输出 $q$ 行,每行输出每次询问的答案。

示例1

输入

3 3 2
rrr
rdd
edr
1 1 1 3
1 1 2 1

输出

2
1

说明

对于第一次询问,子矩阵是 "rrr",需要至少修改 2 个字符才能保证不包含长度超过 1 的回文子串。

对于第二次询问,只需要把第一个字符改成 'd' 或者 'e' 即可,只需要 1 次操作。(改第二行第一列字符也可以)。

 

解题思路

  要保证横向或纵向任意长度大于 $1$ 的连续子串不是回文串,只需保证横向与纵向任意长度为 $2$ 和 $3$ 的连续子串不是回文串即可。

  先考虑横向的情况,由于只有 red 这 $3$ 种字符,因此若要保证横向上不存在长度为 $2$ 和 $3$ 的回文串,那么合法的字符串只有以下 $6$ 种:redredred...edredredr...dredredre...derderder...erderderd...rderderde...,即 rededrdredererdrde 的循环。

  再考虑纵向的情况,假定矩阵的第一行是 redredred... 这种形式,将上一行循环左移或循环右移一个单位作为下一行,就会得到 redredred...
edredredr...
dredredre...
...
redredred...
dredredre...
edredredr...
...
,这种构造方式使得纵向上与横向相同,都不会存在回文串。注意在选定移动的方向后就不能再改变了。同理剩余的 $5$ 种情况。

  由于横向上的字符串有 $6$ 种选择,有 $2$ 种方向构造下一行,因此满足横向与纵向任意长度为 $2$ 和 $3$ 的连续子串不是回文串的矩阵就只有 $6 \times 2 = 12$ 种。对于给定的询问 $(x_1,y_1,x_2,y_2)$,我们只需枚举这 $12$ 种矩阵并统计该区域内不同的单元格的数量,最后取最小值即可,可以预处理 $12$ 个二维前缀和来加速这个过程。

  我写的程序并没有直接把这 $12$ 种矩阵创建出来,而是先分别枚举 $6$ 种字符串 str,再枚举 $2$ 个方向。对于左移方向,str[(i + j) % 3] 就对应矩阵 g[i][j] 上的字符。对于右移方向,str[((j - i) % 3 + 3) % 3] 就对应矩阵 g[i][j] 上的字符。这样就可以知道 $12$ 种矩阵每个格子是哪个字符了。

  最后还要特判 $(x_1,y_1,x_2,y_2)$ 为 $2 \times 2$ 的情况。除了要枚举上面分析到的 $12$ 种矩阵外,还要考虑以下 $6$ 种矩阵:re
er
er
re
rd
dr
dr
rd
ed
de
de
ed
,这是 $12$ 种矩阵里面不存在的状态。

  AC 代码如下,时间复杂度为 $O(n \cdot m + q)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 510;

char g[N][N];
int s[12][N][N];

int get(int k, int x1, int y1, int x2, int y2) {
    return s[k][x2][y2] - s[k][x1 - 1][y2] - s[k][x2][y1 - 1] + s[k][x1 - 1][y1 - 1];
}

int main() {
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%s", g[i] + 1);
    }
    int cnt = 0;
    for (string str : {"red", "edr", "dre", "der", "erd", "rde"}) {
        for (int k = 0; k <= 1; k++, cnt++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    int w = 0;
                    if (!k && str[(i + j) % 3] != g[i][j]) w = 1;
                    if (k && str[((j - i) % 3 + 3) % 3] != g[i][j]) w = 1;
                    s[cnt][i][j] = w;
                }
            }
        }
    }
    for (int k = 0; k < cnt; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                s[k][i][j] += s[k][i - 1][j] + s[k][i][j - 1] - s[k][i - 1][j - 1];
            }
        }
    }
    while (k--) {
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        int ret = 1e9;
        for (int i = 0; i < cnt; i++) {
            ret = min(ret, get(i, x1, y1, x2, y2));
        }
        if (x2 - x1 == 1 && y2 - y1 == 1) {
            for (string str : {"re", "er", "rd", "dr", "ed", "de"}) {
                int sum = 0;
                if (str[0] != g[x1][y1]) sum++;
                if (str[1] != g[x1][y2]) sum++;
                if (str[1] != g[x2][y1]) sum++;
                if (str[0] != g[x2][y2]) sum++;
                ret = min(ret, sum);
            }
        }
        printf("%d\n", ret);
    }
    
    return 0;
}

 

参考资料

【题目讲解】2024牛客寒假算法基础集训营6 讲题:https://www.bilibili.com/video/BV1WZ42127qx/

posted @ 2024-02-29 21:12  onlyblues  阅读(23)  评论(0编辑  收藏  举报
Web Analytics