题目大意
一堆桌子被排列成n×m(范围为[1,500])的矩形,a[i][j]=1表示位置(i,j)处有桌子,0表示没有。我们要寻找满足这些条件的子矩形:
- 该子矩形的四条边上没有空位;
- 子矩形中的空位与桌子的数量之差不超过1(不包括侧面的桌子);
- 子矩形的长度和宽度必须大于1。
有多少个子矩形可以满足要求?
解题思路
500×500的矩阵,我们最多可以跑
的算法。那么对付这样的枚举子矩阵题,我们的惯用套路就是用前缀和来维护了。我们在原矩阵中把0则当做-1,求出要求的0和1的差,记录前缀和。注意:前缀和不能算上边界的1。
一种种查找的方法就是枚举上下行边界,再在每列查找。每个子矩阵要求四条边上都是1,所以我们枚举上下边界后,找一段在这两行都是1的列区间,再在这个区间里找。
枚举每一列,如果这一列也都是1,就可以统计进去。那么每次找到合法的列,查询前面的前缀和是否有和它相差1以内的,计入答案。然后把自己的前缀和加入统计。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=25e4+10,M=510;
int a[M][M],b[M][M],f[N*2],s[M],n,m;
long long ans;
int main()
{
int x,i,j,k,l;
s[0]=N;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if(!a[i][j]) a[i][j]=-1;
b[i][j]=b[i-1][j]+a[i][j];
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
{
x=1;
for(k=1;k<=m;k++)
{
if(a[i][k]!=1 || a[j][k]!=1)
{
for(l=x;l<=k;l++)
if(b[j][l]-b[i-1][l]==j-i+1) f[s[l]]--;
x=k+1,s[k]=N;
}
else
{
if(b[j][k]-b[i-1][k]==j-i+1)
ans+=f[s[k-1]]+f[s[k-1]+1]+f[s[k-1]-1];
s[k]=s[k-1]+b[j-1][k]-b[i][k];
if(b[j][k]-b[i-1][k]==j-i+1) f[s[k]]++;
}
}
for(k=x;k<=m;k++)
if(b[j][k]-b[i-1][k]==j-i+1) f[s[k]]--;
}
printf("%lld\n",ans);
}
京公网安备 11010502036488号