JZOJ4817. 【NOIP2016提高A组五校联考4】square 二维rmq

题目大意

给你一个NM的矩阵,矩阵中每个位置为01。现在有Q组询问,每组询问给出x1,y1,x2,y2,表示询问左上角为(x1,y1),右下角为(x2,y2)的矩阵内。最大的全部为1的正方形矩阵变长是多少。

N,M1000
Q106

解题思路

首先一个很直观的思路就是先预处理出数组p以每个个点为右下角,最大的全1正方形矩阵的边长是多少。这个比较简单可以直接设dp[i][j]表示表示以(i,j)为右下角的最大边长。转移十分简单:

dp[i][j]=min(min(dp[i1][j],dp[i][j1]),dp[i1][j1])+1

那么对于一个矩形中的最大边长我们可以先二分一个长度len。然后询问预处理中左上角为x1+len1y1+len1,右下角为x2,y2这个矩阵中p数组的最大值是否大于len,如果大于即合法,否则不合法。

那么现在的问题就变成了求一个矩阵中的最大值,那么很自然的就想到了二维rmq,介绍如下。

二维rmq

我们可以设rmq[i][j][x][y]表示以(x,y)为右下角,左上角为(x2i+1y2j+1)的矩阵中的最大值。原理跟一维rmq类似。对于特殊的i=0j=0的情况,根一维rmq一样

if(i==0) rmq[i][j][k][l]=max(rmq[i][j1][k][l],rmq[i][j1][k][lPow[j1]]);if(j==0) rmq[i][j][k][l]=max(rmq[i1][j][k][l],rmq[i1][j][kPow[i1]][l])

否则我们只需把当前矩形拆成两个求最大值
rmq[i][j][k][l]=max(rmq[i][j1][k][l],rmq[i][j1][k][lPow[j1]])

而询问时只要把询问的矩形分成四个矩形询问就可以了
int lx=log[x2x1+1],ly=log[y2y1+1];Ans=max(max(rmq[lx][ly][x2][y2],rmq[lx][ly][x1+Pow[lx]1][y1+Pow[ly]1]),max(rmq[lx][ly][x2][y1+Pow[ly]1],rmq[lx][ly][x1+Pow[lx]1][y2]));

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 1e3 + 5, MAXQ = 10;

int n, m, t, rmq[MAXQ][MAXQ][MAXN][MAXN], log[MAXN], Pow[MAXQ];

void Prepare() {
    Pow[0] = 1;
    for (int i = 0, j = 1; j < MAXN; i ++, j *= 2) log[j] = i;
    for (int i = 1; i < MAXN; i ++) log[i] = max(log[i], log[i - 1]);
    for (int i = 1; i < MAXQ; i ++) Pow[i] = Pow[i - 1] * 2;
    for (int i = 2; i <= n; i ++)
        for (int j = 2; j <= m; j ++) 
            if (rmq[0][0][i][j]) rmq[0][0][i][j] = min(min(rmq[0][0][i - 1][j], rmq[0][0][i][j - 1]), rmq[0][0][i - 1][j - 1]) + 1;
    for (int i = 0; i < MAXQ; i ++)
        for (int j = 0; j < MAXQ; j ++) {
            if (i == 0 && j == 0) continue;
            for (int k = Pow[i]; k <= n; k ++)
                for (int l = Pow[j]; l <= m; l ++) {
                    if (i == 0) rmq[i][j][k][l] = max(rmq[i][j - 1][k][l], rmq[i][j - 1][k][l - Pow[j - 1]]); else
                    if (j == 0) rmq[i][j][k][l] = max(rmq[i - 1][j][k][l], rmq[i - 1][j][k - Pow[i - 1]][l]); else 
                        rmq[i][j][k][l] = max(rmq[i][j - 1][k][l], rmq[i][j - 1][k][l - Pow[j - 1]]);
                }
        }
}

int Get(int x1, int y1, int x2, int y2) {
    int lx = log[x2 - x1 + 1], ly = log[y2 - y1 + 1];
    return max(max(rmq[lx][ly][x2][y2], rmq[lx][ly][x1 + Pow[lx] - 1][y1 + Pow[ly] - 1]), max(rmq[lx][ly][x2][y1 + Pow[ly] - 1], rmq[lx][ly][x1 + Pow[lx] - 1][y2]));
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++) 
            scanf("%d", &rmq[0][0][i][j]);
    Prepare();
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        int l = 1, r = min(y2 - y1 + 1, x2 - x1 + 1), Ans = 0;
        while (l <= r) {
            int Mid = (l + r) >> 1;
            if (Get(x1 + Mid - 1, y1 + Mid - 1, x2, y2) >= Mid) Ans = Mid, l = Mid + 1; else
                r = Mid - 1;
        }
        printf("%d\n", Ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值