P1950 长方形_NOI导刊2009提高(2)[单调栈][贡献法]

P1950 长方形_NOI导刊2009提高(2)

题意:给你\(n\times m\)的矩形,求没有*的子矩形数量。\(1\leq n,m\leq 1000\)

数据比较弱的题目是luoguP1191,\(1\leq n,m \leq 100\)。甚至可以用\(O(n^4)\)水过。

这道题的最优解法\(O(n^2)\)是这样的:

  • 枚举子矩形的底边,预处理出每一列可以往上延伸的长度\(h[i]\)
  • 跑两次单调栈,第一次找到每个数右边第一个小于它的数\(l[j]\),第二次找到每个数左边第一个小于等于它的数\(r[j]\)
  • 枚举每一列,那么这个点对答案的贡献就是\((j - l[j] + 1) \times (r[j] - j + 1) \times h[i]\)

其实枚举的\(j\)就是看看如果这个点是整个大矩形的短板,能够为答案贡献多少个矩形。高度就是从\(1\)\(h[i]\),共\(h[i]\)个。

两次单调栈要有一次小于等于!同时有多个短板的话可能会算重,我们这样算就不重不漏了。

有一个小细节容易错:在单调栈统计的时候,记录的答案不是那个\(i\),而是相应的\(i-1\)\(i+1\)。手摸一下就知道了。

代码:

#include<bits/stdc++.h>

const int maxn = 1005;
char ch[maxn][maxn];
int h[maxn];
int g1[maxn], g2[maxn];
int n, m;
long long ans;
void init() {
    // print
    //for(int i = 1; i <= m; i++) printf("%d ", h[i]);
    //printf("\n");
    memset(g1, 0, sizeof g1);
    memset(g2, 0, sizeof g2);
    std::stack<int> sta;// 找到第一个小于我的
    for(int i = 1; i <= m; i++) {
        while(!sta.empty() && h[sta.top()] > h[i]) {
            g2[sta.top()] = i - 1; sta.pop();// 是前面的那个
        }
        sta.push(i);
    }
    while(!sta.empty()) {
        g2[sta.top()] = m; sta.pop();
    }
    // print
    //for(int i = 1; i <= m; i++) printf("%d ", g2[i]);
    //printf("\n");
    for(int i = m; i >= 1; i--) {
        while(!sta.empty() && h[sta.top()] >= h[i]) {
            g1[sta.top()] = i + 1; sta.pop();// 是前面的那个
        }
        sta.push(i);
    }
    while(!sta.empty()) {
        g1[sta.top()] = 1; sta.pop();
    }
    // print
    //for(int i = 1; i <= m; i++) printf("%d ", g1[i]);
    //printf("\n");
}
int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%s", ch[i] + 1);
        for(int j = 1; j <= m; j++) {
            if(ch[i][j] == '*') h[j] = 0;
            else h[j]++;
        }
        init();
        for(int j = 1; j <= m; j++) {
            int temp = (j - g1[j] + 1) * (g2[j] - j + 1) * h[j];
            // print
            //printf("%d ", temp);
            ans += temp;
        }
        //printf("\n");
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2019-02-19 16:36  Garen-Wang  阅读(240)  评论(0编辑  收藏  举报