JZOJ2748. 【2012中山市选】最大立方体空间

14 篇文章 0 订阅
2 篇文章 0 订阅

题目大意

给出一个L*W*H的空间,里面有N个长方体,求剩余空间里的最大立方体的棱长。

二维解法

其实可以去看一下另一道题叫纪念碑(JZOJ4238)

二分答案len,把所有矩形的右上边界延长len,把原框的左下边长延长len。
这里写图片描述

显然,如果某次延长以后仍有空隙,那么把边界还原后一定有空间为len*len的正方形。
这里写图片描述

既然还有空隙,那么仍可以继续延长,直到刚好没有空隙是就是答案。

至于怎么去找空隙,可以把矩形的左右边界求出来,左边+1右边-1,扫描线+线段树维护。
如果某次线段树中的最小值为0,就说明有空隙。

三维解法

其实只要把线段树改成线段树套线段树,维护一个面就行了。
然后矩形扩张时要三个面都扩张。

树套树的实现

鉴于这题比较特殊(维护一个面),所以可以直接多加一维来维护面。
每次如果长大于宽,就把长折半,否则把宽折半。
显然这样做每个面都是固定的。
最后到了目标面以内就直接修改。

code

#include <iostream>
#include <cstdio>
#include <cstring>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define Max_Len 2001
using namespace std;

int X1[Max_Len];
int Y1[Max_Len];
int Z1[Max_Len];
int X2[Max_Len];
int Y2[Max_Len];
int Z2[Max_Len];
int x1[Max_Len];
int y1[Max_Len];
int x2[Max_Len];
int y2[Max_Len];
int z[Max_Len];
int sz[Max_Len];
int tr[8000000][2];
int T,N,L,W,H,i,j,k,l,r,mid,N2,tttt;

void swap(int &a,int &b)
{
    int c=a;
    a=b;
    b=c;
}

void qsort(int l,int r)
{
    int i,j,k,mid;

    i=l;
    j=r;
    mid=z[(l+r)/2];

    while (i<=j)
    {
        while (z[i]<mid) i++;
        while (z[j]>mid) j--;

        if (i<=j)
        {
            swap(x1[i],x1[j]);
            swap(y1[i],y1[j]);
            swap(x2[i],x2[j]);
            swap(y2[i],y2[j]);
            swap(z[i],z[j]);
            swap(sz[i],sz[j]);

            i++;
            j--;
        }
    }

    if (l<j) qsort(l,j);
    if (i<r) qsort(i,r);

    return;
}

void down(int t)
{
    if (tr[t][1])
    {
        tr[t][0]+=tr[t][1];

        tr[t*2][1]+=tr[t][1];
        tr[t*2+1][1]+=tr[t][1];

        tr[t][1]=0;
    }
}

void change(int t,int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2,int s)
{
    int mid;

    if ((X1<=x1) && (Y1<=y1) && (x2<=X2) && (y2<=Y2))
    {
        tr[t][1]+=s;
        down(t);

        return;
    }

    down(t*2);
    down(t*2+1);

    if ((x2-x1)>(y2-y1))//折半搜索
    {
        mid=(x1+x2)/2;
        if ((X1<mid) && (x1<mid)) change(t<<1,x1,y1,mid,y2,X1,Y1,X2,Y2,s);
        if ((mid<X2) && (mid<x2))  change((t<<1)+1,mid,y1,x2,y2,X1,Y1,X2,Y2,s);
    }
    else
    {
        mid=(y1+y2)/2;
        if ((Y1<mid) && (y1<mid)) change(t<<1,x1,y1,x2,mid,X1,Y1,X2,Y2,s);
        if ((mid<Y2) && (mid<y2))  change((t<<1)+1,x1,mid,x2,y2,X1,Y1,X2,Y2,s);
    }

    tr[t][0]=min(tr[t*2][0],tr[t*2+1][0]);
}

bool pd(int t)
{
    int i,j;

    memset(tr,0,sizeof(tr));
    if (t)//把初始面扩张
    {
        change(1,0,0,L,W,0,0,L,t,1);
        change(1,0,0,L,W,0,0,t,W,1);
    }

    j=1;
    fo(i,t,H-1)//依次扫描
    {
        while ((j<=N2) && (z[j]<=i))
        {
            change(1,0,0,L,W,x1[j],y1[j],x2[j],y2[j],sz[j]);
            j++;
        }

        if (!tr[1][0])
        return 1;
    }

    return 0;
}

int main()
{
    scanf("%d",&T);
    fo(tttt,1,T)
    {
        scanf("%d%d%d%d",&N,&L,&W,&H);N2=N*2;
        fo(i,1,N)
        scanf("%d%d%d%d%d%d",&X1[i],&Y1[i],&Z1[i],&X2[i],&Y2[i],&Z2[i]);

        l=0;
        r=min(min(L,W),H);
        while (l<r)
        {
            mid=(l+r)/2;

            fo(i,1,N)//求每个面
            {
                x1[i*2-1]=X1[i];
                y1[i*2-1]=Y1[i];
                x2[i*2-1]=X2[i]+mid;
                y2[i*2-1]=Y2[i]+mid;
                z[i*2-1]=Z1[i];
                sz[i*2-1]=1;

                x1[i*2]=X1[i];
                y1[i*2]=Y1[i];
                x2[i*2]=X2[i]+mid;
                y2[i*2]=Y2[i]+mid;
                z[i*2]=Z2[i]+mid;
                sz[i*2]=-1;
            }
            qsort(1,N2);

            if (pd(mid))
            l=mid+1;
            else
            r=mid;
        }

        printf("Case %d: %d\n",tttt,l);
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值