【2017.11.25普及组模拟】Bob

Description

不但奶牛想建造房子,作为一名建筑师的Bob也想建造自己的房子。他买了块土地,问题是土地的地形不平整。
这块土地的形状就像一个矩形,长N米,宽M米。我们将它看作是由N×M个小正方形组成。(见图) Bob的房子的形状也是矩形的,其边和土地的边是平行的,而且其顶点与某个小正方形的顶点重合。为了使房子保持平衡,Bob要选具有相同高度的小方格建造房子。
(土地被分成多个小方格。房屋的两个可能建造的位置如右图有红色和蓝色。)
请计算Bob建造房子的方案数。
这里写图片描述

Input

第 1 行输入包含N和M;
接下来的N行,每行包含M个整数A[i][j],表示该方格的土地高度。

Output

输出共一行一个整数,即任务中所需的答案。

Sample Input

【输入样例1】
5 3
2 2 2
2 2 1
1 1 1
2 1 2
1 2 1
【输入样例2】
4 3
1 1 1
1 1 1
2 2 2
2 2 2

Sample Output

【输出样例1】
27
【输出样例2】
36

Data Constraint

对于10%的数据: 1≤N,M≤50;1≤A[i][j]≤10;
对于30%的数据: 1≤N,M≤500;1≤A[i][j]≤10,000;
对于100%的数据:1≤N,M≤1,000;1≤A[i][j]≤1,000,000,000;

Solution

最先想到的方法是无法得到任何分数的n^3*m^3的方法。
但是,我们发现这题是可以用单调队列的。
我们首先找到每个点可以向上跟他相同的个数。
我们可以发现这样就会是一个这样的图形(对于一行来说):
这里写图片描述
假设我们这一行搜到了第2个,很明显以当前(2,1)这一个为右下角,我们可行的左下角有(1,4),(1,3),(1,2),(1,1),(2,3),(2,2),(2,1)。也就是说,以(2,1)为右下角得到的贡献就是7。
再看(3,1),我们以当前这个点为右下角,会发现(1,4)是不可以做他的左上角的,因为中间缺了一块,那么说明,当我们发现第二列比第一列要小的时候,我们所能够加的就少了,也就是少了之前所有比他高的高度。我们可以用一个单调队列,记录下这些高度,然后当我们发现比上一个低的话就往前找所有比他小的,下一次就少加这么多,并每次把比他高的改成和他一样。
每一行都这么做,就好了。

#include<cstdio>
#include<iostream>
using namespace std;
long long high[1010][1010];
long long num[1010][1010];
long long n,m,t;
long long sum,ans;
long long d[1010];
int main()
{
    freopen("bob.in","r",stdin);
    freopen("bob.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    int i,j,k;
    for (i=1;i<=n;++i)
        for (j=1;j<=m;++j)
            scanf("%lld",&high[i][j]);
    for (i=1;i<=n;++i)
        for (j=1;j<=m;++j)
        {
            num[i][j]=1;
            if(high[i][j]==high[i-1][j]) num[i][j]+=num[i-1][j];    
        }
    ans=0;
    for (i=1;i<=n;++i)
    {
        t=sum=0;
        for (j=1;j<=m;++j)
        {
            if(high[i][j]!=high[i][j-1]) t=sum=0;
            d[++t]=num[i][j];
            sum+=num[i][j];
            k=t-1;
            while(k>0&&d[k]>d[t]) 
            {
                sum-=(d[k]-d[t]);
                d[k]=d[t];
                k--;
            }
            ans+=sum;   
        }
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值