[Codeforces Round #619][Codeforces 1301E. Nanosoft]

题目链接:1301E - Nanosoft

题目大意:给定一个\(n\times n\)的四色矩阵,要求回答\(q\)次询问:求一个子矩阵中最大的满足条件的正方形。一个正方形满足条件当且仅当其边长为偶数且把他均分成四块后,每个\(\frac{1}{4}\)大小的正方形都等于对应的颜色(左上为'R',右上为'G',左下为'Y',右下为'B')。\(n,m\le 500, q\le 300000\)

题解:第一眼看过去还以为是什么奇怪数据结构题,结果发现并不需要...

   通过观察可以发现,以一个点为左上角的正方形最多只会有一个,这个结论可以很容易证明出来,考虑以该点为左上角的子矩阵,假设目前正方形大小为\(x\),那么这个子矩阵的第一行前\(\frac{x}{2}\)列一定是'R',第一行第\(\frac{x}{2}+1\)列一定是'G',那么无论\(x\)是加或减,都不能让这个正方形满足条件。

   这样我们就可以考虑如何求出一个点为左上角的合法正方形的大小。我们可以进行一个\(O(n^3)\)的判断,即同时枚举左上角的位置和大小,然后\(O(1)\)判断是否满足条件。判断的方式有很多种,我的方法是直接给每个位置赋值,把“RGYB”分别设成对应的\({0,1,10^6,10^{12}}\),这样如果四个分别区间和等于对应的值就是合法的。当然也可以直接开四个数组分别记录每个颜色,求四次二维前缀和也可以做到这点。

   求出每个点为左上角时对应的答案后,就要考虑如何回答询问。由于对于同一次询问,不同的正方形大小会导致合法左上角的区间发生变化,鉴于CF的机子跑得快,可以考虑\(O(n)\)回答每次询问。

   对每个可能的答案\(k\),开一个大小为\(n\times n\)的数组记录每个点是否可以作为一个大小为\(k\)的正方形的左上角,区间求和后就能\(O(1)\)判断每个区间内是否有合法正方形的左上角。回答询问时对不同的正方形大小进行不同的询问即可。

   时间复杂度:预处理\(O(n^3)\),回答询问\(O(nq)\)

 

#include<bits/stdc++.h>
using namespace std;
#define N 505
#define LL long long
LL n,m,q,a[N][N],s[N][N],K[4]={0,1,1000000,1000000000000ll},o;
int f[N/2][N][N];
LL get()
{
    char ch=getchar();
    while(ch!='R' && ch!='G' && ch!='Y' && ch!='B')
      ch=getchar();
    if(ch=='R')return 0;
    if(ch=='G')return 1;
    if(ch=='Y')return K[2];
    return K[3];
}
bool check(LL x,LL y,LL X,LL Y,LL k)
{
    LL res=s[X][Y]-s[x-1][Y]-s[X][y-1]+s[x-1][y-1];
    if(o)cout<<x<<" "<<y<<" "<<X<<" "<<Y<<" "<<k<<" "<<res<<endl;
    return res==k*(X-x+1)*(Y-y+1);
}
void init()
{
    for(LL i=1;i<=n;i++)
      for(LL j=1;j<=m;j++)
        a[i][j]=get();
    for(LL i=1;i<=n;i++)
      for(LL j=1;j<=m;j++)
        s[i][j]=a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    for(LL i=1;i<=n;i++)
      for(LL j=1;j<=m;j++)
        if(a[i][j])continue;
        else
          {
          //if(i==2 && j==8)o=1;
          LL res=0;
          for(LL k=1;i+2ll*k-1<=n && j+2ll*k-1<=m;k++)
            if(check(i,j,i+k-1,j+k-1,K[0]) && check(i,j+k,i+k-1,j+2ll*k-1,K[1])
            && check(i+k,j,i+2ll*k-1,j+k-1,K[2]) && check(i+k,j+k,i+2ll*k-1,j+2ll*k-1,K[3]))
              {res=k;break;}
          f[res][i][j]=1;
          o=0;
          }
}
bool fuck(LL x,LL y,LL X,LL Y,LL k)
{
    LL res=f[k][X][Y]-f[k][x-1][Y]-f[k][X][y-1]+f[k][x-1][y-1];
    //cout<<x<<" "<<y<<" "<<X<<" "<<Y<<" "<<k<<" "<<res<<endl;
    return res>0;
}
int ask()
{
    LL x,y,X,Y,w,h,ans=0;
    scanf("%lld%lld%lld%lld",&x,&y,&X,&Y);
    w=X-x+1,h=Y-y+1;
    for(LL i=min(w,h)/2;i>=1;i--)
      if(fuck(x,y,X-2ll*i+1,Y-2ll*i+1,i))
        return printf("%lld\n",4ll*i*i),0;
    return printf("0\n"),0;
}
void rua()
{
    for(LL k=1;k*2<=min(n,m);k++)
      for(LL i=1;i+2ll*k-1<=n;i++)
        for(LL j=1;j+2ll*k-1<=m;j++)
          f[k][i][j]=f[k][i][j]+f[k][i-1][j]+f[k][i][j-1]-f[k][i-1][j-1];
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&q);
    init();
    rua();
    while(q--)ask();
}
View Code

 

posted @ 2020-02-14 01:19  DeaphetS  阅读(305)  评论(0编辑  收藏  举报