https://ac.nowcoder.com/acm/contest/5674/J
枚举上边界和下边界,从左到右扫枚举当前的列为右边界,要求有多少个左边界满足左边界也是没有空位的,而且横着的两条边也不为空,而且这个区间的有桌子和没桌子的差值最多是1,那么就维护一个(有桌子-没桌子)数量为值的前缀和,然后把当前列为右边界能连接的到左边界前缀和差1的数量就行了,这个用个桶维护数量就行
#include<bits/stdc++.h>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(int &x){
char c=nc(),f=1;
for(;(c<'0'||c>'9')&&c!=EOF;c=nc()) if(c=='-') f=-1;
if(c==EOF) return EOF;
for(x=0;c>='0'&&c<='9';c=nc()) x=x*10+c-'0';
x*=f;
return 1;
}
int A[510][510],sum[510][510],B[510],ll[510][510];
int cnt[1000000],M=5e5,inf=1e9;
int qsum(int a,int b,int c,int d){
if(a>c||b>d) return 0;
return sum[c][d]+sum[a-1][b-1]-sum[a-1][d]-sum[c][b-1];
}
int f(int a,int b,int c,int d){
if(a>c||b>d) return 0;
int z=(c-a+1)*(d-b+1),x=qsum(a,b,c,d),y=z-x;
return x-y;
}
int main(){
int i,n,m,j,k,x;
//scanf("%d%d",&n,&m);
rd(n);rd(m);
for(i=1;i<=n;i++) for(j=1;j<=m;j++) rd(x),A[i][j]=x;//scanf("%d",&A[i][j]);
for(i=1;i<=n;i++) for(j=1;j<=m;j++) sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+A[i][j];
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
ll[i][j]=ll[i][j-1];
if(A[i][j]==0) ll[i][j]=j;
}
}
int now,a,b;
long long ans=0;
for(i=1;i<=n;i++){
for(j=i+1;j<=n;j++){
for(k=1;k<=m;k++){
if(qsum(i,k,j,k)==j-i+1) B[k]=f(i+1,1,j-1,k);
else B[k]=inf;
}
now=0;
for(k=1;k<=m;k++){
if(B[k]!=inf){
b=max(ll[i][k],ll[j][k])+1;
while(now<b){
if(now&&B[now]!=inf) cnt[B[now]+M]--;
now++;
}
a=B[k]-(j-i-1);
x=cnt[a+M]+cnt[a-1+M]+cnt[a+1+M];
ans+=x;
cnt[B[k]+M]++;
}
}
for(k=1;k<=m;k++) if(B[k]!=inf) cnt[B[k]+M]=0;
}
}
cout<<ans<<endl;
}