错综的统一

错综的统一

题目描述

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

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

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

输入描述:

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

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

接下来的 q 行,每行输入四个正整数 x1,y1,x2,y2 ,代表每次询问的矩形区域的左上角坐标 (x1,y1) 和右下角坐标 (x2,y2)

数据范围:

1n,m500,1q100000

1x1x2n1y1y2m

输出描述:

输出 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 的连续子串不是回文串,只需保证横向与纵向任意长度为 23 的连续子串不是回文串即可。

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

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

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

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

  最后还要特判 (x1,y1,x2,y2)2×2 的情况。除了要枚举上面分析到的 12 种矩阵外,还要考虑以下 6 种矩阵:re
er
er
re
rd
dr
dr
rd
ed
de
de
ed
,这是 12 种矩阵里面不存在的状态。

  AC 代码如下,时间复杂度为 O(nm+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 @   onlyblues  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示