大致题意: 给定一个(n imes m imes k)的木块,其中有一些单位坏了。让你选出一个(a imes a imes b)的木块,求最大的(4ab)。
三部曲
这道题大概可以分成三步:初始化、求答案、旋转。
具体放在代码里就是这样子的:
Init(),Work(),Ro(),Init(),Work(),Ro(),Init(),Work();
其中旋转这一部分是比较简单的,可以直接看代码。而初始化、求答案这两步就让我们一个一个进行分析。
初始化
考虑这种三维的东西特别麻烦,肯定要想办法把它给搞成二维的。
于是我们枚举一维(设为(z))作为(b)的一维,则另两维(设为(x,y))就是(a imes a)这两维。(这也是为什么要旋转的原因)
我们枚举(z)的每一层,然后对于这一层上每一个格子((i,j)),求出以((i,j))为右下角最大的正方形边长。
显然,((i,j))的最大边长最多也只能取到((i-1,j))和((i,j-1))最大边长的较小值加(1),然后我们从大到小暴力判断是否能取到每一个值,能取到就直接(break)。这样的总复杂度可以保证是(O(n^3))的。
至于如何判断,我们把(P)视作(0),(N)视作(1),则一个区域可以被选择,当且仅当区域内权值和等于区域大小(即全是(1)),那么直接二维前缀和搞一下就行了。
求答案
好,现在我们已经求出每一个格子作为右下角的最大边长。
于是,我们就枚举长方体右下角的(x,y)两维坐标((i,j)),然后求此时的答案。
假设我们在(z)这一维上选取了([l,r])这一区间,那么答案就是(其中(v)表示这个格子的最大边长):
考虑在枚举(i,j)的情况下,(v_{i,j})完全可以看作一个序列(S),即原式相当于:
这个东西应该就非常好搞了吧。。。
写到这里突然把自己原先A掉的做法Hack掉了,看来这题数据是真的水。。。
正确的做法是,我们用单调栈维护每一个关键的最小值,从而求出每一个点可以作为哪一段区间的最小值,最后更新答案。
具体实现详见代码。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 150
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,m,k,ans;char s[N+5][N+5][N+5];
namespace BlockPlayer
{
char t[N+5][N+5][N+5];
I void Ro()//旋转
{
RI i,j,p,x=n;for(i=1;i<=n;++i) for(j=1;j<=m;++j) for(p=1;p<=k;++p) t[i][j][p]=s[i][j][p];
n=m,m=k,k=x;for(i=1;i<=n;++i) for(j=1;j<=m;++j) for(p=1;p<=k;++p) s[i][j][p]=t[p][i][j];
}
int c[N+5][N+5],v[N+5][N+5][N+5];
I void Init()//初始化
{
RI i,j,p,w;for(p=1;p<=k;++p) for(i=1;i<=n;++i) for(j=1;j<=m;++j)
{
c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+(s[i][j][p]=='N'),//二维前缀和
w=min(v[i-1][j][p],v[i][j-1][p])+1;W(c[i][j]-c[i-w][j]-c[i][j-w]+c[i-w][j-w]<w*w) --w;//暴算最大边长
v[i][j][p]=w;
}
}
int S[N+5],l[N+5],r[N+5];
I void Work()//求答案
{
RI i,j,p,t,T;for(i=1;i<=n;++i) for(j=1;j<=m;++j)//枚举右下角
{
for(T=0,v[i][j][k+1]=0,p=1;p<=k+1;++p)
{
l[p]=p;W(T&&v[i][j][S[T]]>=v[i][j][p]) l[p]=l[S[T]],r[S[T--]]=p-1;S[++T]=p;
//往单调栈中加一个数,同时维护每个最小值的区间范围
}
for(p=1;p<=k;++p) Gmax(ans,4*(r[p]-l[p]+1)*v[i][j][p]);//更新答案
}
}
}using namespace BlockPlayer;
int main()
{
RI i,j;for(scanf("%d%d%d",&n,&m,&k),j=1;j<=m;++j) for(i=1;i<=n;++i) scanf("%s",s[i][j]+1);
return Init(),Work(),Ro(),Init(),Work(),Ro(),Init(),Work(),printf("%d
",ans),0;//三部曲
}