Codeforces Round #619 (Div. 2)E思维+二维RMQ

题:https://codeforces.com/contest/1301/problem/E

题意:给个n*m的图形,q个询问,每次询问问询问区间最大的合法logo的面积是多少

分析:由于logo是由4个同色的小正方形组成的,所以我们先考虑一个数组val[i][j]表示[i][j]位置为‘R' , [i+1][j]位置为’Y‘,[i][j+1]位置为’G‘,[i+1][j+1]位置为’B'所能形成的最大图案边长是多少;

   因为这样就只要预处理‘R’正方形沿着左上能形成的最大正方形,“Y”沿着左下能形成的最大正方形,’G‘沿着右上能形成的最大正方形,”B“沿着右下能形成的最大正方形,然后对于位置[i][j]就取min这四者;

   对于询问,我们只要二分答案搜到最大符合条件的就行了,注意这里的二分的mid,假设询问[r1,c1,r2,c2],则搜索访问应该在[r1+mid-1,c1+mid-1,r2-mid,c2-mid]的范围内搜索才是合法的;

   因为我们在二分时要求满足条件的mid就行,所以这里对val进行倍增处理;

   合法性:因为小的合法图案一定在大的合法图案里面,所以对val取的图案也许是超过询问范围,但我们上面已经对询问范围进行如上缩减,所以是合法的

#include<bits/stdc++.h>
using namespace std;
const int M=505;
const int N=10;
int ex[4][M][M];
int n,m,q;
int val[M][M],f[N][N][M][M],lg[M];
char s[M][M];
void RMQ(int n,int m){
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            f[0][0][i+1][j+1]=val[i][j];
    for(int i=2;i<M;i++)
        lg[i]=1+lg[i/2];
    for(int i=1;i<=n;i++)
        for(int k2=1;(1<<k2)<=m;k2++)
            for(int j=1;j<=m-(1<<k2)+1;j++)
                f[0][k2][i][j]=max(f[0][k2-1][i][j],f[0][k2-1][i][j+(1<<(k2-1))]);
    for(int k1=1;(1<<k1)<=n;k1++)
        for(int i=1;i<=n-(1<<k1)+1;i++)
            for(int k2=0;(1<<k2)<=m;k2++)
                for(int j=1;j<=m-(1<<k2)+1;j++)
                    f[k1][k2][i][j]=max(f[k1-1][k2][i][j],f[k1-1][k2][i+(1<<(k1-1))][j]);
}


int query(int x1,int y1,int x2,int y2){
    int k1=lg[x2-x1+1];
    int k2=lg[y2-y1+1];
    x2=x2-(1<<k1)+1;
    y2=y2-(1<<k2)+1;
    return max(f[k1][k2][x1][y1],max(f[k1][k2][x1][y2],max(f[k1][k2][x2][y1],f[k1][k2][x2][y2])));
}
bool check(int r1,int c1,int r2,int c2,int midd){
    r1=r1+midd-1;
    c1=c1+midd-1;
    r2=r2-midd;
    c2=c2-midd;
    if(r1>r2||c1>c2)
        return false;
    return query(r1,c1,r2,c2)>=midd;
}
void color(int fx,int fy,char c,int k){///根据进来的参数不同,组成不同方向相同颜色的最大正方形 
    int stx=0,sty=0;
    if(fx==-1)stx=n-1;
    if(fy==-1)sty=m-1;
    ///while遍历二维数组 
    while(stx<n&&stx>=0){
        while(sty<m&&sty>=0){
            if(s[stx][sty]==c){
                ex[k][stx][sty]=1;
                if(stx-fx>=0&&stx-fx<n&&sty-fy>=0&&sty-fy<m){///正方形区域取最小值 
                    if(s[stx-fx][sty]==c&&s[stx][sty-fy]==c&&s[stx-fx][sty-fy]==c){
                        ex[k][stx][sty]=min(ex[k][stx-fx][sty],
                                         min(ex[k][stx][sty-fy],ex[k][stx-fx][sty-fy]))+1;
                    }
                }
            }
            sty+=fy;
        }
        sty=0;if(fy==-1)sty=m-1;
        stx+=fx;
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=0;i<n;i++)
        scanf("%s",s[i]); 
    //1
    color(1,1,'R',0);
    color(-1,1,'Y',1);
    color(1,-1,'G',2);
    color(-1,-1,'B',3);
    for(int i=0;i<n-1;i++){
        for(int j=0;j<m-1;j++){
            if(s[i][j]=='R'&&s[i+1][j]=='Y'&&s[i][j+1]=='G'&&s[i+1][j+1]=='B'){
                val[i][j]=min(ex[0][i][j],min(ex[1][i+1][j],min(ex[2][i][j+1],ex[3][i+1][j+1])));
            }
        }
    }
    //2
    RMQ(n,m);

    //3
    for(int i=0;i<q;i++){
        int r1,c1,r2,c2,ans=0;
        scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
        int l=0,r=min(r2-r1,c2-c1);
        while(l<=r){
            int midd=(l+r)>>1;
            if(check(r1,c1,r2,c2,midd)){
                ans=midd;
                l=midd+1;
            }
            else
                r=midd-1;
        }
        printf("%d
",4*ans*ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/12348177.html