Description
在麦克雷的面前出现了一个有n*m个格子的矩阵,每个格子用“.”或“#”表示,“.”表示这个格子可以放东西,“#”则表示这个格子不能放东西。现在他拿着1 *2大小的木棒,好奇的他想知道对于一些子矩阵,有多少种放木棒的方案。
Data Constraint
30%:q<=100
100%:q<=10^5,1<=n,m<=500
Solution
我们考虑用矩阵前缀和来解决这道题。设f[i][j]表示左上角为(1,1)右下角为(i,j)的矩阵有多少种放木棒的方案。那么显然f[i][j]首先等于f[i][j-1]+f[i-1][j]-f[i-1][j-1]。现在我们虑(i,j)与(i,j-1)能否放一根木棒和(i,j)与(i-1,j)能否放一根木棒的情况,有的话就分别给f[i][j]+1。对于一个询问区间(x,y)(x1,y1),显然它的初始答案为f[x1][y1]-f[xx][y-1]-f[x-1][yy]+f[x-1][y-1]。但是我们还要考虑边缘情况,即木棒一边在要求区间内,一边却在要求区间外,所以我们再预处理一下每一行的前缀边缘情况和每一列的前缀边缘情况,在答案中减去即可。询问总时间复杂度O(M),预处理O(N^2)。
代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=505;
int n,m,i,t,j,k,l,f[maxn][maxn],x,y,xx,yy,ans;
int b[maxn][maxn],a[maxn][maxn],h[maxn][maxn],u[maxn][maxn];
bool bz[maxn][maxn];
char s[maxn];
int main(){
// freopen("data.in","r",stdin);
scanf("%d%d\n",&n,&m);
for (i=1;i<=n;i++){
scanf("%s\n",s+1);
for (j=1;j<=m;j++)
if (s[j]=='.') bz[i][j]=true;
for (j=1;j<=m;j++){
f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
if (!bz[i][j]) continue;
if (bz[i][j-1]) f[i][j]++,a[i][j]++;
if (bz[i-1][j]) f[i][j]++,b[i][j]++;
}
}
for (i=1;i<=n;i++)
for (j=1;j<=m;j++){
h[i][j]=h[i-1][j]+a[i][j];
u[i][j]=u[i][j-1]+b[i][j];
}
scanf("%d",&m);
for (i=1;i<=m;i++){
scanf("%d%d%d%d",&x,&y,&xx,&yy);
ans=f[xx][yy]-f[x-1][yy]-f[xx][y-1]+f[x-1][y-1];
ans-=u[x][yy]-u[x][y-1];
ans-=h[xx][y]-h[x-1][y];
printf("%d\n",ans);
}
}