CodeForces372B- Counting Rectangles is Fun(dp)

Description
There is an n × m rectangular grid, each cell of the grid contains a single integer: zero or one. Let’s call the cell on the i-th row and the j-th column as (i, j).
Let’s define a “rectangle” as four integers a, b, c, d (1 ≤ a ≤ c ≤ n; 1 ≤ b ≤ d ≤ m). Rectangle denotes a set of cells of the grid {(x, y) :  a ≤ x ≤ c, b ≤ y ≤ d}. Let’s define a “good rectangle” as a rectangle that includes only the cells with zeros.
You should answer the following q queries: calculate the number of good rectangles all of which cells are in the given rectangle.

Input
There are three integers in the first line: n, m and q (1 ≤ n, m ≤ 40, 1 ≤ q ≤ 3·105). Each of the next n lines contains m characters — the grid. Consider grid rows are numbered from top to bottom, and grid columns are numbered from left to right. Both columns and rows are numbered starting from 1.
Each of the next q lines contains a query — four integers that describe the current rectangle, a, b, c, d (1 ≤ a ≤ c ≤ n; 1 ≤ b ≤ d ≤ m).

Output
For each query output an answer — a single integer in a separate line.

Examples
Input
5 5 5
00101
00000
00001
01000
00001
1 2 2 4
4 5 4 5
1 2 5 2
2 2 4 5
4 2 5 3
Output
10
1
7
34
5
Input
4 7 5
0000100
0000010
0011000
0000000
1 7 2 7
3 1 3 1
2 3 4 5
1 2 2 7
2 2 4 7
Output
3
1
16
27
52

题意:
有一个n x m的由0和1组成的矩阵,有q个询问,每次给出左上角和右下角的坐标x1,y1和x2,y2,求这个小矩阵内仅由0构成的矩阵个数。

解法:
这道题询问上下左右固定的矩阵内,一共有多少个仅由0构成的矩阵(此处定义为dp2[up][down][left][right]),如果我们能够计算出上下左右固定的矩阵内,以下边界为起点的仅由0构成的矩阵个数(此处定义为dp1[up][down][left][right]),那么只需要枚举上下左右边界,随后遍历上边界到下边界,每次相加即可得到答案。
为此首先需要一个数组shu[i][j][k],表示第i列从第k行起上溯到第j行一共有多少个以a[k][i]为起点的连续的0。因为要求是连续的,所以只需要在每次k+1时,判断a[k][i]是否为0,是的话shu[i][j][k]=shu[i][j][k-1]+1,否则直接为0。
随后计算dp1,枚举上边界i,下边界j,左边界k和右边界t,每次右边界向右挪动时,只比之前多出了以新增列为右边界的矩阵。此时s从右边界遍历到左边界,用now表示shu[s][i][j],每次now取min(now,shu[s][i][j])(多出了宽度等于s到右边界,高度从1到now的矩阵),然后将now加入答案中,最后再加上dp1[i][j][k][t-1]就可以得到结果。
最后计算dp2,每次询问时直接输出即可。
该方法费时O(n ^ 5),最坏情况在1e8左右,题目给出4000ms,勉强通过(不过最后测下来只有280ms)。上网查了一下发现也有dp套dp的方法,可以进一步降低时间复杂度,达到O(n^4),不过估计临场是想不到的,还是算了。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
using namespace std;

int n,m,q;
char x;
char a[40+5][40+5];
long long shu[40+5][40+5][40+5],dp1[40+5][40+5][40+5][40+5],dp2[40+5][40+5][40+5][40+5];

void cal1(){
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            for(int k=j;k<=n;k++){
                if(a[k][i]=='0')shu[i][j][k]=shu[i][j][k-1]+1;
            }
        }
    }
}

long long cal2(int up,int down,int l,int r){
    long long res=0,now=shu[r][up][down];
    for(int i=r;i>=l;i--){
        now=min(now,shu[i][up][down]);
        res+=now;
    }
    return res;
}

int main()
{
    scanf("%d%d%d%c",&n,&m,&q,&x);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)scanf("%c",&a[i][j]);
        scanf("%c",&x);
    }
    cal1();
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            for(int k=1;k<=m;k++){
                dp1[i][j][k][k]=shu[k][i][j];
                for(int t=k+1;t<=m;t++){
                    dp1[i][j][k][t]=dp1[i][j][k][t-1]+cal2(i,j,k,t);
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            for(int k=1;k<=m;k++){
                for(int t=k;t<=m;t++){
                    for(int s=i;s<=j;s++){
                        dp2[i][j][k][t]+=dp1[i][s][k][t];
                    }
                }
            }
        }
    }
    for(int i=1;i<=q;i++){
        int x1,x2,y1,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%lld\n",dp2[x1][x2][y1][y2]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值