zoukankan      html  css  js  c++  java
  • 【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
    ",4*ans);
    	return 0;
    }
    
  • 相关阅读:
    牛顿插值法及其C++实现
    见鬼吧,拉格朗日插值法
    迭代法与开根号求值(letcode 69)
    多项式计算之秦九韶算法
    (扩展根目录容量方法汇总)把Linux系统迁移到另一个分区或者硬盘
    jdk 动态代理源码分析
    python 随笔
    java 笔记
    enum(枚举类型)
    提高工作效率的准则
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9885690.html
Copyright © 2011-2022 走看看