题目大意
给出一个 n ∗ m ( 1 ≤ n , m ≤ 1000 ) n*m(1 \leq n, m \leq 1000) n∗m(1≤n,m≤1000) 的矩阵,矩阵元素可以为 ‘.’, ‘*’。求出所有的仅有 . . . 构成的矩形的个数。
解题思路
这题应该由两个部分构成,一是如何统计矩形,二是如何不重不漏且在时间复杂度内统计矩形。
统计矩形
若没有 ‘*’ 的限制在矩阵内找有多少个矩形,实际上就是这道题的一个简化版:P2241 统计方形。
考虑矩形的性质,可以由如下角度入手去统计:
- 固定矩形的左上角和右下角,可以唯一确定一个矩形。
- 固定矩阵的一条底边,然后根据高度可以唯一地确定一个矩形。
性质一显然超时,考虑性质二,可以得出一个规律,在 n ∗ m n*m n∗m 的矩阵中有 n ( n + 1 ) 2 ∗ m ( m + 1 ) 2 \frac{n(n + 1)}{2} * \frac{m(m + 1)}{2} 2n(n+1)∗2m(m+1) 个矩形。
解决本题
假设 d [ i ] [ j ] d[i][j] d[i][j] 代表位置 ( i , j ) (i, j) (i,j) 能向上扩展的最长连续 ‘.’ 的长度,实际上也就是上方第一个 ‘*’ 的下一个的位置, d d d 的初始化很好写。
考虑单调栈的经典问题,找水平线上的最大矩形,考虑每一行,把 d d d 当做竖直的宽为一的矩形:每一个位置 j j j 作为底边,求出左边比它矮的第一个矩形的位置 L L L 和右边比它矮的第一个矩形 R R R ,就是说包含当前点的所有区间中,左边可选 j − L j - L j−L 种,右边可选 R − j R - j R−j 种,那么当前位置应该累加的答案数目就是 ( j − L ) ∗ ( R − j ) ∗ d [ i ] [ j ] (j - L) * (R - j) * d[i][j] (j−L)∗(R−j)∗d[i][j]。
但是上述统计方法会重复,如果有连续的一段全为同一个数,这样是重复的,例如 1 1 1 1 1~~1~~1~~1 1 1 1 1。说对于一段区间 [ i , j ] [i, j] [i,j] ,如何选出所有的子区间且不重复?从左到右固定每个 l ( i ≤ l ≤ j ) l(i \leq l \leq j) l(i≤l≤j) ,枚举 r ( l ≤ r ≤ j ) r(l \leq r \leq j) r(l≤r≤j) 即可。参考这个思路,我们求出每个位置左边不大于它的第一个数的位置,右边小于它的第一个数的位置,即可做到不重复。
因为我们是按行进行的,且利用了上述统计矩形的性质二,因此这种统计方法不会漏。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-6;
const int Mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 10;
int d[1005][1005];
int L[1005], R[1005];
int st[1005];
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
char ch;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> ch;
if (ch == '.') d[i][j] = d[i - 1][j] + 1;
}
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
int top = 0;
d[i][0] = d[i][m + 1] = -inf;
st[++top] = 0;
for (int j = 1; j <= m; j++) {
while (d[i][st[top]] > d[i][j]) top--;
L[j] = st[top];
st[++top] = j;
}
top = 0;
st[++top] = m + 1;
for (int j = m; j >= 1; j--) {
while (d[i][st[top]] >= d[i][j]) top--;
R[j] = st[top];
st[++top] = j;
}
for (int j = 1; j <= m; j++) ans += (R[j] - j) * (j - L[j]) * d[i][j];
}
cout << ans << endl;
return 0;
}