P1950. 长方形

考点

  • 单调栈
  • 悬线法

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 50;
ll ans;
char c[maxn][maxn];
int n, m, top, st[maxn], h[maxn], l[maxn], r[maxn];

int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> c[i][j];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) h[j] = c[i][j] == '.' ? h[j] + 1 : 0;
//左开右开区间
//求向右最大延伸,维护不减单调栈
top = 0;
for (int j = 1; j <= m; ++j) {
while (top && h[st[top]] > h[j]) r[st[top--]] = j;
st[++top] = j;
}
while (top) r[st[top--]] = m + 1;
//求向左最大延伸,维护递增单调栈
top = 0;
for (int j = m; j >= 1; --j) {
while (top && h[st[top]] >= h[j]) l[st[top--]] = j;
st[++top] = j;
}
while (top) l[st[top--]] = 0;
for (int j = 1; j <= m; ++j) ans += 1ll * h[j] * (j - l[j]) * (r[j] - j);
}
cout << ans;
return 0;
}

思路

本题是P4147的升级版,请先掌握该题;题目问有多少种剪矩形的办法。

根据悬线法可知,一个最大矩形可以由中线切开,组成的可能性 = 左半部分的可能性 * 右半部分的可能性

本题实际上就是求每个悬线向左和向右的最大扩展距离罢了~

但是要注意不重不漏哟,如下图所示:

  • A向右扩展时可以和P4147一样正常找最大

  • B向左扩展时遇到高度小于等于时就终止,因为A向右扩展时已经找过最大值了

明白了这些后,再看《深进》就懂啦~