牛客网暑期ACM多校训练营(第七场) J.Sudoku Subrectangles (思维)

题目链接

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
Special Judge, 64bit IO Format: %lld

题目描述

You have a n * m grid of characters, where each character is an English letter (lowercase or uppercase, which means there are a total of 52 different possible letters).

A nonempty subrectangle of the grid is called sudoku-like if for any row or column in the subrectangle, all the cells in it have distinct characters.

How many sudoku-like subrectangles of the grid are there?

输入描述:

The first line of input contains two space-separated integers n, m (1 ≤ n, m ≤ 1000).

The next n lines contain m characters each, denoting the characters of the grid. Each character is an English letter (which can be either uppercase or lowercase).

输出描述:

Output a single integer, the number of sudoku-like subrectangles.

示例1

输入

2 3
AaA
caa

输出

11

说明

For simplicity, denote the j-th character on the i-th row as (i, j).

For sample 1, there are 11 sudoku-like subrectangles. Denote a subrectangle
by (x1, y1, x2, y2), where (x1, y1) and (x2, y2) are the upper-left and lower-right coordinates of the subrectangle.

The sudoku-like subrectangles are (1, 1, 1, 1), (1, 2, 1, 2), (1, 3, 1, 3), (2, 1, 2, 1), (2, 2, 2, 2), (2, 3, 2, 3), (1, 1, 1, 2), (1, 2, 1, 3), (2, 1, 2, 2), (1, 1, 2, 1), (1, 3, 2, 3).

示例2

输入

4 5
abcde
fGhij
klmno
pqrst

输出

150

说明

For sample 2, the grid has 150 nonempty subrectangles, and all of them are sudoku-like.

题意:给出一个N*M的字符串矩阵,其中里面的字符只含有52个大小写英文字母,且定义好的数独子矩阵是每不存在相同字母,每不存在相同字母的矩阵,问你在这N*M中有多少个非空子数独矩阵?

题解:挺多细节的题目,处理前缀数组问题挺多,首先我们需要处理处每个位置能向上扩张的范围和向左扩张的范围,之后跑每个点,至于跑每个点的计算方案是计算其作为子矩阵的右下角点的合法数量,起初我只是简单的跑O(52*N*M),认为对于每个点只要在向左扫到最大L_max[ i ][ j ]的过程中,维护最小向上扩张的高度Len即可,可是忽略了一种情况,eg:

ABA

CDE

若是对于E点时,安装我的方法在向左扫到C点时对答案的贡献是2,此时Len=2,可是本应该是为1才对的,没有考虑到上一层A和A不能在一行的情况,无奈只能换一下写法,保留下上一层每个点的最大合法扩张高度,所以改成了一维向下,二维向右的for循环,对于每个点,我都向上扫最大合法向左扩张高度(要求满足一定比上一层该位置的最大合法高度+1还小或相等,且若是该点达不到将left改为0,以便下一层最大扩张高度正确).

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 1100;
char mp[maxn][maxn];
int lmax[maxn][maxn], umax[maxn][maxn];  //预处理出每个位置向上能扩张的最大长度和向左能扩涨的最大长度
int last[maxn];    
int n, m;
ll solve() {
     
    for (int i = 1; i <= n; i++){
        memset(last, 0, sizeof(last));
        for (int j = 1; j <= m; j++) {
            lmax[i][j] = min(lmax[i][j - 1] + 1, j - last[mp[i][j]]);
            last[mp[i][j]] = j;
        }
    }
    for (int j = 1; j <= m; j++) {
        memset(last, 0, sizeof(last));
        for (int i = 1; i <= n; i++) {
            umax[i][j] = min(umax[i - 1][j] + 1, i - last[mp[i][j]]);
            last[mp[i][j]] = i;
        }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) {      
        int left[maxn] = { 0 };        
        for (int j = 1; j <= m; j++) {
            int len = inf;
            for (int k = i; k >= i - umax[i][j] + 1; k--) {
                left[k] = min(left[k] + 1, lmax[k][j]);
                len = min(len, left[k]);
                ans += len;
            }
            for (int k = i - umax[i][j]; k >= i - 51 && k >= 0; k--) left[k] = 0;
        }
    }
    return ans;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%s", mp[i] + 1);
    printf("%lld\n", solve());
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/81571456