NOIP提高组【JZOJ4791】矩阵

本文介绍了一种使用矩阵前缀和的方法来解决在一个由“.”和“#”组成的n*m矩阵中,计算特定子矩阵内1*2木棒的不同放置方案数量的问题。该方法考虑了木棒放置的有效位置,并通过预处理减少了查询时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值