牛客网多校第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\n", ans);
70             }
71             //printf("%d ans\n",ans);
72         }
73     }
74     printf("%lld\n", ans);
75     return 0;
76 }
View Code

猜你喜欢

转载自www.cnblogs.com/zmin/p/9556092.html