繁星
【问题描述】
要过六一了,大川正在绞尽脑汁想送给小伙伴什么礼物呢。突然想起以前拍过一张夜空中的繁星的照片,这张照片已经被处理成黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑。像素(x,y)处是一颗星星,当且仅当,像素( x x x, y y y),( x − 1 x-1 x−1, y y y),( x + 1 x+1 x+1, y y y),( x x x, y − 1 y-1 y−1),( x x x, y + 1 y+1 y+1)都是白色的。因此一个白色像素有可能属于多个星星,也有可能有的白色像素不属于任何一颗星星。但是这张照片具有研究价值,所以大川不想把整张照片都送给小伙伴,而只准备从中裁下一小块长方形照片送给他。但为了保证效果,大川认为,这一小块相片中至少应该有 k k k 颗星星。
现在大川想知道,到底有多少种方法裁下这一小块长方形相片呢?
【输入格式】
从文件 s t a r . i n star.in star.in 中输入数据。
输入的第一行包含三个正整数 n n n, m m m, k k k,意义见题目所示。
接下来 n n n 行,每行一个长度为 m m m 的字符串,字符串仅由’.‘和’‘构成,’.‘表示这个像素为黑色,’'表示这个像素为白色。
【输出格式】
输出到文件 s t a r . o u t star.out star.out 中。
输出的第一行包含一个整数,表示大川有多少种满足题意的裁剪方法。
【样例输入】
5 5 5 6 6 6 3 3 3
∗ ∗ ∗ . . . ***... ∗∗∗...
∗ ∗ ∗ ∗ . . ****.. ∗∗∗∗..
.**.*.
∗ ∗ ∗ ∗ ∗ ∗ ****** ∗∗∗∗∗∗
. ∗ . ∗ ∗ ∗ .*.*** .∗.∗∗∗
【样例输出】
3 3 3
【样例说明】
图中共有 4 4 4 颗星星,分别位于第 2 2 2 行第 2 2 2 列、第 2 2 2 行第 3 3 3 列、第 4 4 4 行第 2 2 2 列、第 4 4 4 行第 5 5 5 列。
有 3 3 3 种符合题意的选择方法(以左上角行列 - 右下角行列方式给出): ( 1 , 1 1,1 1,1)-( 5 , 4 5,4 5,4)
( 1 , 1 1,1 1,1)-( 5 , 5 5,5 5,5) ( 1 , 1 1,1 1,1)-( 5 , 6 5,6 5,6)
【数据规模与约定】
对于 20 20 20%的数据,满足 N N N, M M M<= 20 20 20. 对于 40 40 40%的数据,满足 N , M < = 100 N,M<=100 N,M<=100. 对于 70 70 70%的数据,满足 N , M N,M N,M<= 200 200 200. 对于 100 100 100%的数据,满足 N , M N,M N,M<= 500 500 500, 0 < k < N ∗ M 0<k<N*M 0<k<N∗M.
20 20 20%做法:
枚举左上角、右下角,暴力统计其中的星星个数。O( N 6 N^6 N6)
40 40 40%做法:
首先发现查询矩形内星星个数可以用部分和在O( N 2 N^2 N2)预处理后O( 1 1 1)回答。
枚举左上角、右下角,用部分和计算其中星星个数。O( N 4 N^4 N4)
70 70 70%做法:
枚举子矩阵的左、右边界。然后发现对于某个给定的下边界,可行的上边界必然是连续一段。
因此枚举左、右边界后用部分和维护这一条里的星星,枚举下边界,二分查找上边界。O( N 3 l g N N^3lgN N3lgN)
100 100 100%做法:
发现左右边界确定后,随着下边界的向下移动,上边界也只会向下移动或不动。
于是枚举左、右边界后用部分和维护星星,然后用一个滑窗维护可行的上边界。O( N 3 N^3 N3)
此题十分垃圾,考试时二维前缀和加双指针水过,下面来一发考试代码。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}