• 牛客网多校第7场 J Sudoku Subrectangles 【构造】


    题目:戳这里

    题意:给一个n*m的矩阵,里面由a~z及A~Z构成,问有多少个子矩阵满足任意一行或一列中都没有相同的字母。

    解题思路:左上角和右下角两点可以确定一个矩阵。可以先预处理出来每个点作为一个矩阵的右下角,向左和向上的最长值。然后遍历每个点是右下角的情况,计算该点为右下角时,能构成多少个矩阵。计算方法为:

    1.设右下角为(i,j),它向左的最长值为r[i][j],向上最长之为c[i][j],设左上角为(x,y)。

    2.遍历j~j-r[i][j]+1,维护最小值minn[]。

    3.根据minn[]数组和c[][]数组,找到符合条件的左上角,计入答案。

    这三步操作的原因是,一个符合条件的左上角(x,y),要满足矩形底边上所有点的i-c[i][k]+1>=x,右边上所有点的j-r[k][j]+1>=y。

    代码思路比较绕,写的时候得静下心。

    附ac代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <string>
     6 #include <cmath>
     7 #include <map>
     8 
     9 using namespace std;
    10 typedef long long ll;
    11 const int maxn = 1e3 + 10;
    12 char st[maxn][maxn];
    13 int r[maxn][maxn], c[maxn][maxn];
    14 int pos[maxn];
    15 int minn[maxn];
    16 const int inf = 0x3f3f3f3f;
    17 int main()
    18 {
    19     int n, m;
    20     scanf("%d %d", &n, &m);
    21     for(int i = 1; i <= n; ++i)
    22     {
    23         scanf("%s", st[i] + 1);
    24     }
    25     int len = 0;
    26     int cnt = 0;
    27     for(int i = 1; i <= n; ++i)
    28     {
    29         memset(pos, 0, sizeof(pos));
    30         for(int j = 1; j <= m; ++j)
    31         {
    32                 len = st[i][j] - 'A';
    33                 r[i][j] = j - pos[len];
    34                 r[i][j] = min(r[i][j], r[i][j - 1] + 1);//避免abba时,无法更新pos
    35                 pos[len] = j;
    36         }
    37     }
    38     for(int j = 1; j <= m; ++j)
    39     {
    40         memset(pos, 0, sizeof(pos));
    41         for(int i = 1; i <= n; ++i)
    42         {
    43                 len = st[i][j] - 'A';
    44                 c[i][j] = i - pos[len];
    45                 c[i][j] = min(c[i][j], c[i - 1][j] + 1);//同上
    46                 pos[len] = i;
    47         }
    48     }
    49     ll ans = 0;
    50     for(int i = 1; i <= n; ++i)
    51     {
    52         memset(minn, inf, sizeof(minn));
    53         for(int j = 1; j <= m; ++j)
    54         {
    55             for(int k = j; k >= j - r[i][j] + 1; --k)
    56                 minn[k] = min(minn[k + 1], c[i][k]);//(i,k)点能取到的最远列
    57 
    58             int len = j - r[i][j] + 1;
    59 
    60             for(int k = i; k >= i - c[i][j] + 1; --k)
    61             {
    62                 while(minn[len] < i - k + 1 || r[k][j] < j - len + 1)//倘若点(i,j)的minn值取不到k或(k,j)本身取不到minn,则说明len不符合条件
    63                 {
    64                     ++ len;
    65                     if(len > j) break;
    66                 }
    67                 if(len > j) break;
    68                 ans += j - len + 1;
    69                // printf("%lld ans
    ", ans);
    70             }
    71             //printf("%d ans
    ",ans);
    72         }
    73     }
    74     printf("%lld
    ", ans);
    75     return 0;
    76 }
    View Code
  • 相关阅读:
    栈溢出笔记1.3 准备Shellcode
    聊聊手游的那些惊喜与惊吓
    GIS+=地理信息+容器技术(4)——Docker执行
    与AQS有关的并发类
    HDU 2102 A计划
    生产系统ELK日志采集系统
    datagrip离线安装驱动jar
    oracle无效索引重建
    18年总结及19年展望
    shell符号解释
  • 原文地址:https://www.cnblogs.com/zmin/p/9556092.html
Copyright © 2020-2023  润新知