1. 程式人生 > >【BZOJ2228】[ZJOI2011]禮物(單調棧)

【BZOJ2228】[ZJOI2011]禮物(單調棧)

【BZOJ2228】[ZJOI2011]禮物(單調棧)

題面

BZOJ
洛谷

題解

如果這個玩意不是一個三維立方體,而是一個二維的矩形,讓你在裡面找一個最大正方形,那麼全世界都會做。
丟到三維上?似乎區別也不是很大啦。
我們先把每一層一片一片的剖開考慮,預處理以某個位置為左上角的最大正方形邊長。這個很容易求,可以用單調佇列做到\(O(pqr)\)。接下來列舉某個左上角,把在每一層上的這個邊長全部扣下來,形成一個序列。那麼要求的就是最小值乘以選擇的長度的最大值。這個東西顯然還是可以單調佇列求。
那麼這樣子複雜度就變成了\(O(pqr)\),再分別按照另外兩維切片,就可以考慮出所有位置的答案了。

然而我想了半天怎麼求以某個點為左上角的最大正方形,就像蘿蔔求助,然後被蘿蔔狠狠狠狠狠的批判了一番:“不就搞個變數掃一遍就好了嗎?”,我“???”(智商掉線.jpg,最近智商已經沒了,要找點智商了。)

大概是這樣的:按照某一行來做,從左往右確定每一列的答案,暴力拓展最大的合法正方形,假設邊長為\(N\),那麼往右移一個位置的時候直接從\(N-1\)還是列舉就好了。我實在是太菜了,這都不會。

注意一下他這個字串的讀入順序並不是\(pqr\),而是\(qpr\)

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 155
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
char g[MAX][MAX][MAX];
int s[MAX][MAX][MAX];
int n[MAX][MAX][MAX];
int p[MAX][MAX];
int L[MAX],R[MAX],Q[MAX],H,T;
int a,b,c,ans;
bool check(int b,int c,int x,int y,int N)
{
    if(x+N-1>b||y+N-1>c)return false;
    int xx=x+N-1,yy=y+N-1;
    return p[xx][yy]-p[xx][y-1]-p[x-1][yy]+p[x-1][y-1]==N*N;
}
void Calc(int a,int b,int c)
{
    for(int i=1;i<=a;++i)
    {
        for(int j=1;j<=b;++j)
            for(int k=1;k<=c;++k)
                p[j][k]=s[i][j][k]+p[j-1][k]+p[j][k-1]-p[j-1][k-1];
        for(int j=1;j<=b;++j)
        {
            int N=0;
            for(int k=1;k<=c;++k)
            {
                while(check(b,c,j,k,N+1))++N;
                n[i][j][k]=N;N-=1;
            }
        }
    }
    for(int j=1;j<=b;++j)
        for(int k=1;k<=c;++k)
        {
            T=0;
            for(int i=1;i<=a;++i)
            {
                while(T&&n[Q[T]][j][k]>=n[i][j][k])--T;
                L[i]=Q[T]+1;Q[++T]=i;       
            }
            T=0;
            for(int i=a;i>=1;--i)
            {
                while(T&&n[Q[T]][j][k]>=n[i][j][k])--T;
                R[i]=T?Q[T]-1:a;Q[++T]=i;
            }
            for(int i=1;i<=a;++i)
                ans=max(ans,n[i][j][k]*(R[i]-L[i]+1));
        }
}
int main()
{
    b=read();a=read();c=read();
    for(int i=1;i<=a;++i)
        for(int j=1;j<=b;++j)
            scanf("%s",g[i][j]+1);
    for(int i=1;i<=a;++i)
        for(int j=1;j<=b;++j)
            for(int k=1;k<=c;++k)
                s[i][j][k]=(g[i][j][k]=='N');
    Calc(a,b,c);
    for(int i=1;i<=b;++i)
        for(int j=1;j<=a;++j)
            for(int k=1;k<=c;++k)
                s[i][j][k]=(g[j][i][k]=='N');
    Calc(b,a,c);
    for(int i=1;i<=c;++i)
        for(int j=1;j<=b;++j)
            for(int k=1;k<=a;++k)
                s[i][j][k]=(g[k][j][i]=='N');
    Calc(c,b,a);
    printf("%d\n",4*ans);
    return 0;
}