原题
题目描述
一堆桌子被排列成
的矩形,
表示位置
处有桌子,否则就没有。为了不被老师抓住,他要藏在一个矩形下,这个矩形要满足这些条件
该子矩形的四条边上没有空位;
子矩形中的空位与桌子的数量之差不超过
(不包括侧面的桌子)
子矩形的长度和宽度必须大于
。
有多少个子矩形可以满足要求
输入描述
输入包含两个整数
和
,范围为
,中间用空格隔开。
然后是
行,每行包含
个字符来描述矩形
。
样例1
输入
4 4
1 1 1 1
1 0 1 1
1 1 0 1
1 1 1 1
输出
3
说明
There're two 2*2 rectangle full of "1",and the whole 4*4 rectangle .
样例2
输入
5 5
1 0 1 1 1
1 0 1 0 1
1 1 0 1 1
1 0 0 1 1
1 1 1 1 1
输出
3
思路
因为本题的数据范围还是比较小的,
和
都
,所以可以考虑时间复杂度为
3
的算法。
首先,我们会想到一种
4
的算法
枚举矩阵左上的端点和右下的端点,然后判断一下矩阵周围一圈是不是
,有一个不是
就退出搜索。然后判断一下矩阵除了最外圈的
,
和
数量的差值是否
,如果是,累加到
上。
显然这就是一个单纯的暴力,时间复杂度明显不对,所以还得优化。
首先,我们可以原来
的矩阵转化为
和
的矩阵,然后求一下前缀和。
如果求出一个区间内的数字和的绝对值
,则表示这是一个合法的区间。
我们可以枚举上下行边界,对于每一列扫一遍,用前缀和维护即可,维护时不能算上这个矩阵周围的
。
枚举列的时候,如果这一列也是
,就可以统计进去。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int inf=1<<15;
int a[505][505],b[505][505],c[250005],d[505],n,m,x=1;
int ans;
int main()
{
d[0]=inf;scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%lld",&a[i][j]);if(!a[i][j])a[i][j]=-1;b[i][j]=b[i-1][j]+a[i][j];}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++,x=1)
{
for(int k=1;k<=m;k++)
if(a[i][k]^1||a[j][k]^1)
{
for(int l=x;l<=k;l++)if(b[j][l]-b[i-1][l]==j-i+1)c[d[l]]--;
x=k+1,d[k]=inf;
}
else
{
if(b[j][k]-b[i-1][k]==j-i+1)ans+=c[d[k-1]]+c[d[k-1]+1]+c[d[k-1]-1];
d[k]=d[k-1]+b[j-1][k]-b[i][k];
if(b[j][k]-b[i-1][k]==j-i+1)c[d[k]]++;
}
for(int k=x;k<=m;k++)if(b[j][k]-b[i-1][k]==j-i+1)c[d[k]]--;
}
printf("%d\n",ans);
}