E. 矩阵 2020计蒜之道决赛

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;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/109348408