(初中OJ)2242. 【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;

题解

听说正解是什么单调栈,只是吓人的,其实很简单。
对于每一位,维护up[i,j]表示这一列从上开始,有几个连续的与本位相同的数(持续至本位,包括本身,即初值为1)。那么对于一行的up[i]来说,我们再开一个栈来维护从前到目前连续的区间内,相应的矩形个数,举个例子:
这里写图片描述
额,有点丑,将就着看。
那么假设这些都是同一颜色的,我们做到3、5、9、12、16这一行,1、6号方块是没用的,那么我们可以直接删去,即最终处理出来的栈为2,2,3,3,4。
接着我们要压栈,大概就是将它压成
原数 个数
2 2
3 2
4 1
记得当颜色不一样时整个栈清零。
每次做一位统计一位。

var
        n,m,i,j,k,tot:longint;
        ans,jl:int64;
        up,a:array[0..1000,0..1000]of longint;
        z:array[0..1000,1..2]of longint;
begin
        readln(n,m);
        for i:=1 to n do
        begin
                for j:=1 to m do
                begin
                        read(a[i,j]);
                        up[i,j]:=1;
                        if a[i,j]=a[i-1,j] then
                                inc(up[i,j],up[i-1,j]);
                        if j=1 then
                        begin
                                fillchar(z,sizeof(z),0);
                                tot:=1;
                                z[tot,1]:=up[i,j];
                                z[tot,2]:=1;
                                inc(ans,up[i,j]);
                                continue;
                        end;
                        if a[i,j]<>a[i,j-1] then
                        begin
                                fillchar(z,sizeof(z),0);
                                tot:=1;
                                z[tot,1]:=up[i,j];
                                z[tot,2]:=1;
                                inc(ans,up[i,j]);
                                continue;
                        end;
                        if up[i,j]<z[tot,1] then
                        begin
                                jl:=1;
                                for k:=tot downto 1 do
                                begin
                                        if up[i,j]>=z[k,1] then
                                                break;
                                        inc(jl,z[k,2]);
                                end;
                                if up[i,j]<z[1,1] then
                                begin
                                        tot:=1;
                                        z[tot,1]:=up[i,j];
                                        z[tot,2]:=jl;
                                end
                                else
                                begin
                                        if up[i,j]=z[k,1] then
                                        begin
                                                tot:=k;
                                                inc(z[tot,2],jl);
                                        end
                                        else
                                        begin
                                                tot:=k+1;
                                                z[tot,1]:=up[i,j];
                                                z[tot,2]:=jl;
                                        end;
                                end;
                                for k:=1 to tot do
                                        inc(ans,z[k,1]*z[k,2]);
                                continue;
                        end;
                        if up[i,j]=z[tot,1] then
                                inc(z[tot,2])
                        else
                        begin
                                inc(tot);
                                z[tot,1]:=up[i,j];
                                z[tot,2]:=1;
                        end;
                        for k:=1 to tot do
                                inc(ans,z[k,1]*z[k,2]);
                end;
                readln;
        end;
        writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值