1. 题面梗概
中译中 : 给你一个黑白格点图,问你这里面一共能圈出多少个纯白的长方形?
2. 逻辑推导
直觉 : 暴力数肯定不行,得换个思路。如果我固定长方形的底部 在某一行,往上看,这不就是一个个柱状图 吗?这就是一个 悬线法 + 贡献法 的典型应用。
我们分析一下,如果只有一行,问题就是要找有多少子区间,这很简单。但如果问的是二维的问题呢?我们可以枚举每一行作为长方形的底边。
对于第 i i i 行的每个点 j j j ,它能向上延伸的连续白格高度 h [ j ] h[j] h [ j ] 就是一个悬线。
接下来我们考虑怎么统计,在第 i i i 行,我们看到的是一个柱状图,在这个柱状图中,每个局部最低点都限制了以它为顶点的长方形宽度。
3. 代码实现
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;const int maxn = 1000 + 50 ;char mat[maxn][maxn];int h[maxn];int n, m;int main () { cin >> n >> m; long long ans = 0 ; for ( int i = 1 ; i <= n; ++ i ) { for ( int j = 1 ; j <= m; ++ j ) { char tmp; cin >> tmp; if ( tmp == '.' ) h[j] ++; else h[j] = 0 ; } for ( int j = 1 ; j <= m; ++ j ) { int cur = h[j]; for ( int k = j; k <= m; ++ k ) { if ( !h[k] ) break ; cur = min ( cur, h[k] ); ans += cur; } } } cout << ans << '\n' ; return 0 ; }
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <bits/stdc++.h> using namespace std;using ll = long long ;const int maxn = 1000 + 5 ;int n, m;int h[maxn], l[maxn], r[maxn];ll solve ( ) { stack< int > stk; for ( int i = 1 ; i <= m ; ++ i ) { while ( !stk.empty ( ) && h[stk.top ( )] >= h[i] ) stk.pop ( ); l[i] = stk.empty ( ) ? 0 : stk.top ( ); stk.push ( i ); } while ( !stk.empty ( ) ) stk.pop ( ); for ( int i = m; i >= 1 ; -- i ) { while ( !stk.empty ( ) && h[stk.top ( )] > h[i] ) stk.pop ( ); r[i] = stk.empty ( ) ? m + 1 : stk.top ( ); stk.push ( i ); } ll res = 0 ; for ( int i = 1 ; i <= m; ++ i ) { res += 1ll * h[i] * ( i - l[i] ) * ( r[i] - i ); } return res; }int main ( ) { cin >> n >> m; ll ans = 0 ; for ( int i = 1 ; i <= n; ++ i ) { for ( int j = 1 ; j <= m; ++ j ) { char c; cin >> c; if ( c == '.' ) h[j] ++; else h[j] = 0 ; } ans += solve ( ); } cout << ans << '\n' ; return 0 ; }
4. 复盘