Description

【NOIP2016提高A组五校联考4】square_RMQ

Solution

比赛的时候就想到了要二分,还有像RMQ一样的弄,就像有一道题叫做妮厨的愤怒(我的博客上有)一样,思想十分的简单,但是我没有打完TAT。
比赛之后推倒重写。

一个f数组

首先需要一个f数组f[i][j]表示以(i,j)为右下角的最大正方形的边长。
f[i][j]=min(f[i−1][j],min(f[i−1][j−1],f[i][j−1]))+1
这个十分的显然。

二分和二维RMQ

然后,最大最小值明显是用二分的思想来处理。
二分出一个mid表示答案,然后只需要在(x+mid-1,y+mid-1)到(xx,yy)这个范围内找一个最大值就好了。
只用最大值而已,那么明显RMQ值最合适不过的。
g[k][l][i][j]表示i向右2k−1个点,j向下2l−1个点的最大的f[i][j]
然后,这里又有一个需要反思的地方了。
我在用RMQ在(x,y)到(xx,yy)范围内找最大值的时候一开始是用log2的方法的,然后这样时间就爆了。
后来才发现我的RMQ没有学好(主要是用的不多,这题又要卡常数),然后用了个快点的方法:比如一维的RMQ中在[x,y]中找一个最大值,首先求出q=log(x−y+1)/log(2),然后最大值等于max(g[x][q],g[x+2q][q]),因为只用求最大值,所以两个区间覆盖并没有什么关系。
那么二维的RMQ也是类似的。
但是之后,还是过不了,只有90分。

卡常

首先是预处理的卡常:
1、k和l我一开始是开到10的,其实9就好了(我没有用换底公式)。
2、为什么k和l要打在前面呢?因为这样预处理的时候i和j的范围就变成了2i......n和2j......m,范围会小一点。
3、去最大值优化(这个还是很有用的)。
4、去函数优化。
5、其实上面的都不是关键,最重要的是这个优化(开了我3个小时)。电脑搞这个log的速度是非常慢的,所以与其用高级的换底公式还不如直接暴力算出log(速度才O(9)而已,比打log函数快多了)。
这些优化打了,肯定能过。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1007;
int i,j,k,l,t,n,m,cas,r,mid,ans,p,q,yi,er1,lef,rig;
int x,y,xx,yy;
int a[maxn][maxn],f[maxn][maxn];
int g[11][11][maxn][maxn],er[11];
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 pan(int xx,int yy,int x,int y){
int i,j,t=0,k=0,ans=0,q;
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(g[t][k][x+(1<<t)-1][yy]>ans)ans=g[t][k][x+(1<<t)-1][yy];
if(g[t][k][xx][y+(1<<k)-1]>ans)ans=g[t][k][xx][y+(1<<k)-1];
return ans;
}
int main(){
// freopen("fan.in","r",stdin);
// freopen("fan.out","w",stdout);
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
scanf("%d%d",&n,&m);
er[0]=1;
fo(i,1,10)er[i]=er[i-1]*2;
fo(i,1,n)fo(j,1,m)a[i][j]=get();
for(i=1;i<=n;i++)for(j=1;j<=m;j++){
if(a[i][j]){
g[0][0][i][j]=g[0][0][i-1][j];
if(g[0][0][i][j-1]<g[0][0][i][j])g[0][0][i][j]=g[0][0][i][j-1];
if(g[0][0][i-1][j-1]<g[0][0][i][j])g[0][0][i][j]=g[0][0][i-1][j-1];
g[0][0][i][j]++;
}
}
for(i=0;(1<<i)<=n;i++){
for(j=0;(1<<j)<=m;j++){
if(i==0&&j==0)continue;
for(k=er[i];k<=n;k++)
for(l=er[j];l<=m;l++){
if(j==0){
g[i][j][k][l]=g[i-1][j][k][l];
if(g[i-1][j][k-(1<<(i-1))][l]>g[i][j][k][l])g[i][j][k][l]=g[i-1][j][k-(1<<(i-1))][l];
}
else {
g[i][j][k][l]=g[i][j-1][k][l];
if(g[i][j-1][k][l-(1<<(j-1))]>g[i][j][k][l])g[i][j][k][l]=g[i][j-1][k][l-(1<<(j-1))];
}
}
}
}
for(scanf("%d",&cas);cas;cas--){
x=get(),y=get(),xx=get(),yy=get();
if(pan(xx,yy,x,y)==0){
printf("0\n");
continue;
}
l=0,r=xx-x+1;
if(yy-y+1<r)r=yy-y+1;
while(l<r){
mid=(l+r+1)/2;
if(pan(xx,yy,x+mid-1,y+mid-1)>=mid)l=mid;else r=mid-1;
}
printf("%d\n",l);
}
}