https://nanti.jisuanke.com/t/49109
学习自https://www.cnblogs.com/BOZHAO/p/13880947.html
求最大全1子矩阵可以左开右开这样对应高度找最大宽度
但是有时候涉及全1子矩阵中间的计数,需要左开右闭,即左边严格小于,右边<=
这题我们统计以第i行为底边,必须经过(i,j)这个点的满足的矩阵数
对于最高高度为h[j],左边界为l[j],右边界为r[j],首先我们可以得到(r[j]-l[j]+1)*h[j]这一整个矩阵中所有的满足条件的子矩阵,由于我们计数必须让他经过(i,j),所以要减去最高高度为h[j]的两个不经过(i,j)的部分,宽度分别是j-l[j]和r[j]-j
b[i][j]表示必须要经过(1,1),i*j矩阵中有多少个子矩阵满足要求,那么sum[i][j]就表示必须要经过(1,1)(1,2)...(1,j)的满足条件的子矩阵的总和
感觉这个不重复有点难以解释,只能画图理解一下为什么没有重复。。。。推荐2323213这个高度序列来理解
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=1010;
int n,top;ll ans;
int h[maxl],s[maxl],l[maxl],r[maxl];
int a[maxl][maxl];
ll b[maxl][maxl],sum[maxl][maxl];
char c[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",c+1);
for(int j=1;j<=n;j++)
a[i][j]=c[j]-'0';
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i%j==0 || j%i==0)
b[i][j]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
sum[i][j]=sum[i][j-1]+b[i][j];
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
h[j]=(a[i][j]>0)?h[j]+1:0;
s[0]=0;top=0;
for(int j=1;j<=n;j++)
{
while(top>0 && h[s[top]]>=h[j])
top--;
l[j]=s[top]+1;
s[++top]=j;
}
s[0]=n+1;top=0;
for(int j=n;j>=1;j--)
{
while(top>0 && h[s[top]]>h[j])
top--;
r[j]=s[top]-1;
s[++top]=j;
}
for(int j=1;j<=n;j++)
ans+=sum[h[j]][r[j]-l[j]+1]-sum[h[j]][j-l[j]]-sum[h[j]][r[j]-j];
}
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}