[整体二分][二维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
posted @ 2020-02-20 10:51  AE酱  阅读(285)  评论(0编辑  收藏  举报