The Escape Plan of Groundhog
题目描述:
一个深黑的夜晚,土拨鼠不开心。直到老师来了,他才发现自己忘了做作业。为了挽救生命,他必须立即躲在桌子下面,以免被老师重击。
他班上的课桌以
的矩形排列。
表示位置
处有桌子,否则就没有。
为了不被老师抓住,他决定只在以下情况下藏在一个矩形下:
该子矩形的四边没有空位;
因为土拨鼠很胖,所以空间不能太小;但是,如果空缺过多,很容易找到土拨鼠,因此不应有太多空缺。因此,他希望子矩形中的空位数量与表的数量之差不超过
(不包括侧面的表)。
子矩形的长度和宽度必须大于
。
土拨鼠现在想知道:有多少个满足要求的子矩形?
输入描述:
输入包含两个
和
,它们在
范围内,并用空格隔开。
然后跟随
行,每个行包含
个字符来描述矩形
输出描述:
输出包含一个整数,描述满足要求的子矩形的数量。
样例:
样例输入1:
4 4
1 1 1 1
1 0 1 1
1 1 0 1
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
样例输出2:
3
思路:
暴力枚举+前缀和。
看到这题的数据范围就想到可以用暴力
直接求解。
首先我们最容易想到的暴力方法:
枚举每个矩阵的左上角的点和右下角的点,然后再遍历边看看是否符合条件,时间复杂度大概
,明显超时。
那么有没有更好的暴力方法呢?当然是有的,我们可以先看一下这道题:和为k的连续区间我们可以用前缀和来优化我们的暴力:
我们把矩阵中0当做-1,然后求每一列的前缀和,再枚举上下两条边,找到两行都是一的一段,通过之前求的前缀和来判断矩阵是否合法。
我们首先找到都是1的两列,然后在答案上加上这个矩阵的贡献。
:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=25e4+5;
const int MAXM=505;
int n,m,a[MAXM][MAXM],qian[MAXM][MAXM],dp[MAXN*2],pky[MAXM],ans;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
scanf("%d",a[i]+j);
if(!a[i][j]) --a[i][j];
qian[i][j]=a[i][j]+qian[i-1][j];//每一列的前缀和
}
pky[0]=MAXN;
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j){//枚举上下行
int pre=1;
for(int k=1;k<=m;++k){//枚举列
if(a[i][k]^1||a[j][k]^1){
for(int l=pre;l<=k;++l)//再枚举列
if(qian[j][l]-qian[i-1][l]==j-i+1)
--dp[pky[l]];
pre=k+1;
pky[k]=MAXN;
continue;
}
if(qian[j][k]-qian[i-1][k]==j-i+1)
ans+=dp[pky[k-1]]+dp[pky[k-1]-1]+dp[pky[k-1]+1];
pky[k]=pky[k-1]+qian[j-1][k]-qian[i][k];
if(qian[j][k]-qian[i-1][k]==j-i+1) ++dp[pky[k]];
}
for(int l=pre;l<=m;++l)
if(qian[j][l]-qian[i-1][l]==j-i+1)
--dp[pky[l]];
}
printf("%d\n",ans);
}