2018.06.29 NOIP模拟 繁星(前缀和)

时间:2021-09-05 10:03:37

繁星

【问题描述】

要过六一了,大川正在绞尽脑汁想送给小伙伴什么礼物呢。突然想起以前拍过一张夜空中的繁星的照片,这张照片已经被处理成黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑。像素(x,y)处是一颗星星,当且仅当,像素(xxx,yyy),(x−1x-1x−1,yyy),(x+1x+1x+1,yyy),(xxx,y−1y-1y−1),(xxx,y+1y+1y+1)都是白色的。因此一个白色像素有可能属于多个星星,也有可能有的白色像素不属于任何一颗星星。但是这张照片具有研究价值,所以大川不想把整张照片都送给小伙伴,而只准备从中裁下一小块长方形照片送给他。但为了保证效果,大川认为,这一小块相片中至少应该有 kkk 颗星星。

现在大川想知道,到底有多少种方法裁下这一小块长方形相片呢?

【输入格式】

从文件 star.instar.instar.in 中输入数据。

输入的第一行包含三个正整数 nnn,mmm,kkk,意义见题目所示。

接下来 nnn 行,每行一个长度为 mmm 的字符串,字符串仅由’.‘和’‘构成,’.‘表示这个像素为黑色,’'表示这个像素为白色。

【输出格式】

输出到文件 star.outstar.outstar.out 中。

输出的第一行包含一个整数,表示大川有多少种满足题意的裁剪方法。

【样例输入】

555 666 333

∗∗∗...***...∗∗∗...

∗∗∗∗..****..∗∗∗∗..

.**.*.

∗∗∗∗∗∗******∗∗∗∗∗∗

.∗.∗∗∗.*.***.∗.∗∗∗

【样例输出】

333

【样例说明】

图*有 444 颗星星,分别位于第 222 行第 222 列、第 222 行第 333 列、第 444 行第 222 列、第444 行第 555 列。

有 333 种符合题意的选择方法(以左上角行列 - 右下角行列方式给出): (1,11,11,1)-(5,45,45,4)

(1,11,11,1)-(5,55,55,5) (1,11,11,1)-(5,65,65,6)

【数据规模与约定】

对于 202020%的数据,满足 NNN,MMM<=202020. 对于 404040%的数据,满足 N,M&lt;=100N,M&lt;=100N,M<=100. 对于 707070%的数据,满足 N,MN,MN,M<=200200200. 对于 100100100%的数据,满足 N,MN,MN,M<=500500500,0&lt;k&lt;N∗M0&lt;k&lt;N*M0<k<N∗M.

202020%做法:

枚举左上角、右下角,暴力统计其中的星星个数。O(N6N^6N6)

404040%做法:

首先发现查询矩形内星星个数可以用部分和在O(N2N^2N2)预处理后O(111)回答。

枚举左上角、右下角,用部分和计算其中星星个数。O(N4N^4N4)

707070%做法:

枚举子矩阵的左、右边界。然后发现对于某个给定的下边界,可行的上边界必然是连续一段。

因此枚举左、右边界后用部分和维护这一条里的星星,枚举下边界,二分查找上边界。O(N3lgNN^3lgNN3lgN)

100100100%做法:

发现左右边界确定后,随着下边界的向下移动,上边界也只会向下移动或不动。

于是枚举左、右边界后用部分和维护星星,然后用一个滑窗维护可行的上边界。O(N3N^3N3)

此题十分垃圾,考试时二维前缀和加双指针水过,下面来一发考试代码。

#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<set>
using namespace std;
#define N 505
inline long long read(){
	long long ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		ans=(ans<<3)+(ans<<1)+ch-'0';
		ch=getchar();
	}
	return ans*w;
}
inline void write(long long x){
	if(x<0){
		x=-x;
		putchar('-');
	}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
int n,m,t,b[N],sum[N][N];
long long cnt=0;
bool map[N][N],ok[N];
char s[N];
inline int total(int x1,int y1,int x2,int y2){return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];}
inline void solve1(){
	for(int len=1;len<=n;++len)
		for(int i=len+1;i<n;++i){
			for(int j=1;j<=m;++j)b[j]=sum[i][j]-sum[i-len][j];
			if(b[m]<t)continue;
			int l=2,r=2;
			while(r<=m-1){
				if(b[r]-b[l-1]>=t){
					cnt+=m-r;
					++l;
				}
				else ++r;
			}
		}
}
int main(){
	freopen("star.in","r",stdin);
	freopen("star.out","w",stdout);
	n=read();
	m=read();
	t=read();
	for(int i=1;i<=n;++i){
		scanf("%s",s);
		for(int j=0;j<m;++j){
			if(s[j]=='*')map[i][j+1]=true;
			else map[i][j+1]=false;
		}
	}
	for(int i=2;i<=n;++i)
		for(int j=2;j<=m;++j){
			if(map[i][j]&&map[i-1][j]&&map[i+1][j]&&map[i][j-1]&&map[i][j+1])sum[i][j]=1;
			sum[i][j]=sum[i-1][j]+sum[i][j-1]+sum[i][j]-sum[i-1][j-1];
		}
	if(sum[n][m]<t){
		printf("0");
		return 0;
	}
	solve1();
	write(cnt);
	return 0;
}