题目链接: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();
}
来源:https://www.cnblogs.com/DeaphetS/p/12306097.html
