[整体二分][二维RMQ] Codeforces 1301E Nanosoft
题目大意
给定一个\(N \times M(1 \leq N,M \leq 500)\)的网格,每个格子的颜色为红黄蓝绿之一。\(Nanosoft\)徽标是一个正方形,且恰好被分成4个大小相等的正方形,左上角的颜色是红色,右上角的颜色是绿色,左下角的颜色是黄色,右下角的颜色是蓝色,如:
这些是\(Nanosoft\)徽标,
这些不是\(Nanosoft\)徽标
现在有\(Q(1 \leq Q \leq 3 \times 10^5)\)个询问,每次询问一个子矩形中最大的\(Nanosoft\)徽标的面积。
Sample Input
5 5 5
RRGGB
RRGGY
YYBBG
YYBBR
RBBRG
1 1 5 5
2 2 5 5
2 2 3 3
1 1 3 5
4 4 5 5
Sample Output
16
4
4
4
0
样例解释
题解
容易发现,答案具有单调性。如果子矩形中存在一个较大的\(Nanosoft\)徽标,那么一定存在比它小的\(Nanosoft\)徽标。
不妨以红色方块的右下角为\(Nanosoft\)徽标的基准点,我们可以\(O(NM)\)预处理出以每一个点为基准点的\(Nanosoft\)徽标的最大边长。然后对于所有询问去整体二分,假设当前二分到的\(Nanosoft\)徽标的半边长为\(a\),询问的子矩形为\((x_1,y_1,x_2,y_2)\),则若基准点在\((x_1+a,y_1+a,x_2-a-1,y_2-a-1)\)这个子矩形内的\(Nanosoft\)徽标的最大边长大于等于\(a+1\),则该询问的答案大于等于\(a+1\),否则该询问的答案小于等于\(a\)。
对于子矩形内的最大值,用二维RMQ去处理。
时间复杂度\(O\left(NM+NM l o gNlogM+Qlog\left(\frac{min\left(N,M\right)}{2}\right)\right)\)
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define RG register int
#define LL long long
const int inf=1e9;
template<typename elemType>
inline void Read(elemType &T){
elemType X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
T=(w?-X:X);
}
struct Que{int X1,Y1,X2,Y2,ID;};
Que QUE[300010],Q1[300010],Q2[300010];
int Mat[502][502][4];
int LSum[502][502][4],RSum[502][502][4],USum[502][502][4],DSum[502][502][4];
int RR[502][502],GG[502][502],YY[502][502],BB[502][502],Ex[502][502];
int Ans[300010];
int N,M,Q;
inline void Input(){
Read(N);Read(M);Read(Q);
char c;
for(RG i=1;i<=N;++i){
for(RG j=1;j<=M;++j){
scanf("%c",&c);
if(c=='R') Mat[i][j][0]=1;
else if(c=='G') Mat[i][j][1]=1;
else if(c=='Y') Mat[i][j][2]=1;
else if(c=='B') Mat[i][j][3]=1;
}
scanf("%c",&c);
}
for(RG i=1;i<=Q;++i){
QUE[i].ID=i;
Read(QUE[i].X1);Read(QUE[i].Y1);
Read(QUE[i].X2);Read(QUE[i].Y2);
}
if(N==1||M==1){while(Q--) printf("0\n");}
return;
}
inline void Init(){
for(RG i=1;i<=N;++i){
for(RG j=1;j<=M;++j){
for(RG k=0;k<4;++k){
if(!Mat[i][j][k]) LSum[i][j][k]=0;
else LSum[i][j][k]=LSum[i][j-1][k]+Mat[i][j][k];
if(!Mat[i][M-j+1][k]) RSum[i][M-j+1][k]=0;
else RSum[i][M-j+1][k]=RSum[i][M-j+2][k]+Mat[i][M-j+1][k];
if(!Mat[i][j][k]) USum[i][j][k]=0;
else USum[i][j][k]=USum[i-1][j][k]+Mat[i][j][k];
if(!Mat[N-i+1][j][k]) DSum[N-i+1][j][k]=0;
else DSum[N-i+1][j][k]=DSum[N-i+2][j][k]+Mat[N-i+1][j][k];
}
}
}
for(RG i=1;i<=N;++i){
for(RG j=1;j<=M;++j){
RR[i][j]=min(RR[i-1][j-1]+1,min(LSum[i][j][0],USum[i][j][0]));
GG[i][M-j+1]=min(GG[i-1][M-j+2]+1,min(RSum[i][M-j+1][1],USum[i][M-j+1][1]));
YY[N-i+1][j]=min(YY[N-i+2][j-1]+1,min(LSum[N-i+1][j][2],DSum[N-i+1][j][2]));
BB[N-i+1][M-j+1]=min(BB[N-i+2][M-j+2]+1,min(RSum[N-i+1][M-j+1][3],DSum[N-i+1][M-j+1][3]));
}
}
for(RG i=1;i<=N;++i)
for(RG j=1;j<=M;++j)
Ex[i][j]=min(min(RR[i][j],GG[i][j+1]),min(YY[i+1][j],BB[i+1][j+1]));
}
int maxv[12][12][502][502];
int pre[502];
void Init_RMQ(){
for(RG i=1;i<=N;i++)
for(RG j=1;j<=M;j++)
maxv[0][0][i][j]=Ex[i][j];
pre[2]=pre[3]=1;
for(RG i=4,up=max(N,M);i<=up;i++) pre[i]=pre[i>>1]+1;
int up1=pre[N]+1,up2=pre[M]+1;
for(RG l1=0;l1<=up1;l1++){
for(RG l2=0;l2<=up2;l2++){
if(!l1&&!l2) continue;
for(RG i=1;(i+(1<<l1)-1)<=N;i++){
for(RG j=1;(j+(1<<l2)-1)<=M;j++){
if(l2)maxv[l1][l2][i][j]=max(maxv[l1][l2-1][i][j],maxv[l1][l2-1][i][j+(1<<(l2-1))]);
else maxv[l1][l2][i][j]=max(maxv[l1-1][l2][i][j],maxv[l1-1][l2][i+(1<<(l1-1))][j]);
}
}
}
}
}
int Query(int x1,int y1,int x2,int y2){
if(x1>x2 || y1>y2) return 0;
if(x1==0 || y1==0 || x2==0 || y2==0) return 0;
int p=pre[x2-x1+1],q=pre[y2-y1+1];
int ans=-inf;
ans=max(maxv[p][q][x1][y1],maxv[p][q][x1][y2-(1<<q)+1]);
ans=max(ans,max(maxv[p][q][x2-(1<<p)+1][y1],maxv[p][q][x2-(1<<p)+1][y2-(1<<q)+1]));
return ans;
}
int x1,x2,y1,y2;
void Solve(int L,int R,int x,int y){
if(x>y) return;
if(L==R){
for(register int i=x;i<=y;++i)
Ans[QUE[i].ID]=(L<<1)*(L<<1);
return;
}
int mid=(L+R)>>1,cnt1=0,cnt2=0;
for(register int i=x;i<=y;++i){
int temp=Query(QUE[i].X1+(mid+1)-1,QUE[i].Y1+(mid+1)-1,QUE[i].X2-(mid+1),QUE[i].Y2-(mid+1));
if(temp<mid+1) Q1[++cnt1]=QUE[i];
else Q2[++cnt2]=QUE[i];
}
for(register int i=1;i<=cnt1;++i)
QUE[x+i-1]=Q1[i];
for(register int i=1;i<=cnt2;++i)
QUE[x+cnt1+i-1]=Q2[i];
Solve(L,mid,x,x+cnt1-1);
Solve(mid+1,R,x+cnt1,y);
return;
}
int main(){
Input();
if(N==1||M==1) return 0;
Init();
Init_RMQ();
Solve(0,min(N/2,M/2),1,Q);
for(RG i=1;i<=Q;++i)
printf("%d\n",Ans[i]);
return 0;
}
// RG
// YB