2020暑期牛客多校训练营第九场(J)The Escape Plan of Groundhog(暴力,前缀和)

The Escape Plan of Groundhog

原题请看这里

题目描述:

一个深黑的夜晚,土拨鼠不开心。直到老师来了,他才发现自己忘了做作业。为了挽救生命,他必须立即躲在桌子下面,以免被老师重击。
他班上的课桌以 N × M N×M N×M的矩形排列。 a i j = 1 {a_ {ij} = 1} aij=1表示位置 ( i , j ) {(i,j)} ij处有桌子,否则就没有。
为了不被老师抓住,他决定只在以下情况下藏在一个矩形下:
该子矩形的四边没有空位;
因为土拨鼠很胖,所以空间不能太小;但是,如果空缺过多,很容易找到土拨鼠,因此不应有太多空缺。因此,他希望子矩形中的空位数量与表的数量之差不超过 1 {1} 1(不包括侧面的表)。
子矩形的长度和宽度必须大于 1 {1} 1
土拨鼠现在想知道:有多少个满足要求的子矩形?

输入描述:

输入包含两个 N {N} N M {M} M,它们在 [ 1 , 500 ] {[1,500]} [1500]范围内,并用空格隔开。
然后跟随 n n n行,每个行包含 m m m个字符来描述矩形 ( a i j ∈ { 0 , 1 } ) {(a_ {ij} \in \{0,1 \})} aij{0,1}

输出描述:

输出包含一个整数,描述满足要求的子矩形的数量。

样例:

样例输入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

思路:

暴力枚举+前缀和。
看到这题的数据范围就想到可以用暴力 O ( n 3 ) O(n^3) O(n3)直接求解。
首先我们最容易想到的暴力方法:
枚举每个矩阵的左上角的点和右下角的点,然后再遍历边看看是否符合条件,时间复杂度大概 O ( n 4 ) O(n^4) O(n4),明显超时。
那么有没有更好的暴力方法呢?当然是有的,我们可以先看一下这道题:和为k的连续区间我们可以用前缀和来优化我们的暴力:
我们把矩阵中0当做-1,然后求每一列的前缀和,再枚举上下两条边,找到两行都是一的一段,通过之前求的前缀和来判断矩阵是否合法。
我们首先找到都是1的两列,然后在答案上加上这个矩阵的贡献。

A C AC AC C o d e Code Code:

#include
#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);
}

你可能感兴趣的:(暴力,前缀和)