NOIP提高组【JZOJ4817】square

5 篇文章 0 订阅
1 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们首先可以预处理出f[i][j]表示以(i,j)为右下角的最大正方形的边长。这显然可以在输入时处理出来。 f[i][j]=min(f[i1][j1],f[i1][j],f[i][j1])+1 。对于一个询问(x,y,xx,yy),我们其实是可以二分答案的。假设我们当前二分出来的答案为mid,那么我们就可以在(x+mid-1,y+mid-1,xx,yy)这个区间那里找一个最大正方形,若最大正方形的边长大于mid就往右走。那么现在问题是:如何在(x+mid-1,y+mid-1,xx,yy)这个区间内找一个最大的f[i][j]呢?我们想到用二维rmq来解决。设g[x][y][i][j]表示(i- 2x+1 ,j- 2y+1 ,i,j)这个区间内的最大的f值。然后设t= log2xx(x+mid1) ,k= log2yy(y+mid1) ,那么一个询问(x+mid-1,y+mid-1,xx,yy)的中的最大f值则是 max(g[t][k][xx][yy],g[t][k][x+mid1+2t][y+mid1+2k],g[t][k][x+mid1+2t][yy],g[t][k][xx][y+mid1+2k]) 。(若看不懂的话可以手动画一下图,画一下就懂了。)注意一下,log的运算在c++中是很慢的,建议手打,否则你会像博主一样卡了3小时的常还是90分……

代码

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1005;
int a[maxn][maxn],n,i,t,j,k,l,m,p,x,y,xx,yy,r,mid,l1;
int f[maxn][maxn],g[11][11][maxn][maxn],ans;
int pan(int x,int y){
    int i,j,t=0,k=0;
    while (x+(1<<(t+1))-1<=xx) t++;
    while (y+(1<<(k+1))-1<=yy) k++;
    ans=g[t][k][xx][yy];
    if (g[t][k][x+(1<<t)-1][y+(1<<k)-1]>ans) ans=g[t][k][x+(1<<t)-1][y+(1<<k)-1];
    if (ans<g[t][k][x+(1<<t)-1][yy]) ans=g[t][k][x+(1<<t)-1][yy];
    if (ans<g[t][k][xx][y+(1<<k)-1]) ans=g[t][k][xx][y+(1<<k)-1];
    return ans;
}
int get(){
    char ch=getchar();int x=0;
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
int main(){
    freopen("square.in","r",stdin);freopen("square.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
            if (a[i][j]) f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
            g[0][0][i][j]=f[i][j];
        }
    l1=log(n)/log(2);
    p=log(m)/log(2);
    for (i=0;i<=l1;i++)
        for (j=0;j<=p;j++){
            if (!i && !j) continue;
            for (x=(1<<i);x<=n;x++)
                for (y=(1<<j);y<=m;y++){
                    if (i){
                        if (g[i-1][j][x][y]>g[i-1][j][x-(1<<(i-1))][y]) g[i][j][x][y]=g[i-1][j][x][y];
                        else g[i][j][x][y]=g[i-1][j][x-(1<<(i-1))][y];
                    }
                    else{
                        if (g[i][j-1][x][y]>g[i][j-1][x][y-(1<<(j-1))]) g[i][j][x][y]=g[i][j-1][x][y];
                        else g[i][j][x][y]=g[i][j-1][x][y-(1<<(j-1))];
                    }
                }
        }
    scanf("%d",&m);
    while (m){
        scanf("%d%d%d%d",&x,&y,&xx,&yy);
        //x=get();y=get();xx=get();yy=get();
        l=0;r=min(yy-y+1,xx-x+1);
        while (l<r){
            mid=(l+r+1)/2;
            if (pan(x+mid-1,y+mid-1)>=mid) l=mid;
            else r=mid-1;
        }
        printf("%d\n",l);
        m--;
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值