• 洛谷10月月赛Round.1| P3400 仓鼠窝[单调栈]


    题目描述

    萌萌哒的Created equal是一只小仓鼠,小仓鼠自然有仓鼠窝啦。

    仓鼠窝是一个由n*m个格子组成的行数为n、列数为m的矩阵。小仓鼠现在想要知道,这个矩阵中有多少个子矩阵!(实际上就是有多少个子长方形嘛。)比如说有一个2*3的矩阵,那么1*1的子矩阵有6个,1*2的子矩阵有4个,1*3的子矩阵有2个,2*1的子矩阵有3个,2*2的子矩阵有2个,2*3的子矩阵有1个,所以子矩阵共有6+4+2+3+2+1=18个。

    可是仓鼠窝中有的格子被破坏了。现在小仓鼠想要知道,有多少个内部不含被破坏的格子的子矩阵!

    输入输出格式

    输入格式:

    第一行两个正整数n和m,分别表示仓鼠窝的行数n、列数m。

    接下来n行,每行m个数,每个数代表对应的格子,非0即1。若为0,表示这个格子被破坏;反之代表这个格子是完好无损的。

    输出格式:

    仅一个正整数,表示未被破坏的子矩阵的个数。

    输入输出样例

    输入样例#1:
    3 4
    1 1 1 1
    1 0 1 1
    1 1 0 1
    输出样例#1:
    26

    说明

    本题时限2s,内存限制256M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。

    No    n=    m=    备注
    1    2    2    无
    2    3    3    无
    3    5    5    无
    4    10    10    无
    5    2000    2000    所有格子均未被破坏
    6    3000    3000    所有格子均未被破坏
    7    2500    3000    有且仅有一个格子被破坏
    8    3000    2500    有且仅有一个格子被破坏
    9    200    200    无
    10    500    500    无
    11    500    500    无
    12    500    500    无
    13    1000    1000    无
    14    1000    1000    无
    15    1000    1500    无
    16    2500    2500    无
    17    2500    3000    无
    18    3000    2500    无
    19    3000    3000    无
    20    3000    3000    无


    比赛时想了一个做法,然而有巨大漏洞没有发现,结果只得10分但至少发现了以(i,j)为右下角高h长l的矩形里矩形个数为h*l,没有右下角限制就是(1+...+h)*(1+...+L)

    正解好厉害,也是考虑求每个(i,j)为右下角的矩阵个数
    一行一行的求,tot[j]表示j列连续1有几个
    每一行用一个单调栈维护(每一列)矩阵的h和l,如果栈顶的h比当前大,就把栈顶和当前合并,l相加,cnt减去栈顶贡献
    最后cnt是累加的,因为这时cnt保存的这一行h<当前的矩阵,当然也可以到达(i,j),也有这一块贡献
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    const int N=3e3+5;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x;
    }
    int n,m,a[N][N],tot[N];//tot---> lie
    ll ans=0;
    struct data{int h,l;}st[N];
    int top=0;
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read();
        for(int i=1;i<=n;i++){
            ll cnt=0;
            top=0;
            data tmp;
            for(int j=1;j<=m;j++){
                if(!a[i][j]){tot[j]=0;cnt=0;top=0;continue;}
                tmp.h=++tot[j];tmp.l=1;
                while(top&&st[top].h>=tmp.h){
                    tmp.l+=st[top].l;
                    cnt-=st[top].h*st[top].l;
                    top--;
                }
                st[++top]=tmp;
                cnt+=tmp.h*tmp.l;
                //printf("%d %d %lld
    ",i,j,cnt);
                ans+=cnt;
            }
        }
        printf("%lld",ans);
    }




  • 相关阅读:
    函数要多小才够好——谈小函数之道
    vb.net 打字练习
    vb.net 打字练习
    vb.net 打字练习
    unsigned int 与 unsigned long 一样吗?
    epoll使用详解(精髓)
    论epoll的使用 高调coding,低调做人 C++博客
    学习使用epoll The time is passing ITeye技术网站
    ubuntu下sed命令详解 Dicky 开源中国社区
    分享:jquery遍历之children()与find()的区别
  • 原文地址:https://www.cnblogs.com/candy99/p/5931534.html
Copyright © 2020-2023  润新知