题目链接: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)\)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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(); }