【BZOJ】Tet-Tetris 3D-超级线段树

蒟蒻要被线段树折磨吐血而亡了……
传送门:[POI2006]Tet-Tetris 3D


题意

    Task: Tetris 3D “Tetris” 游戏的作者决定做一个新的游戏, 一个三维的版本, 在里面很多立方体落在平面板,一个立方体开始落下直到碰上一个以前落下的立方体或者落地即停止. 作者想改变一下游戏的目的使得它更大众化,在新游戏中你将知道落下的立方体信息以及位置,你的任务就是回答所有立方体落下后最高的方块的高度.所有的立方体在下落过程中都是垂直的并且不会旋转.平板左下角坐标为原点,并且平行于坐标轴。


数据范围

D, S and N ( 1<= N<= 20 000, 1<= D, S <=1 000), 分别表示平板的长和宽以及下落立方体的数目. d, s, w, x and y (1<= d, 0 <=x, d + x<= D, 1 <=s, 0<= y, s + y<= S, 1<= w <=100 000), 分别表示立方体的长\宽\高以及落下的左下角坐标, 长和宽都是平行于平板坐标轴的,落下后立方体着地的四个角坐标分别为: (x, y), (x + d, y), (x, y + s) and (x + d, y + s).


题解

    本蒟蒻目前只得了十分,还成功把WA改成了RE还改不回来了。
    之前的博文里写到了二维树状数组,这里似乎是更高级的东西。因为是三维了!不由想起了三位偏序。
    而对于这种高维问题,怎么用线段树解决呢?
    那么来玩树套树吧!
    对于其中一维建线段树,每一个节点以另一维建一颗线段树,维护的标记就是第三维的最大值,本题中即为高度。
    具体看代码。最好不要看我的代码,会又WA又RE的。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e3+10;
const int M=(N*N)<<2;
int n,S,D,cnt;ll ans=0;
int d,s,w,x,y;
int root[N<<2];
ll mx[M],mxx[M],inq[M],inqq[M];
int ls[N<<2],rs[N<<2];

inline ll max(ll x,ll y){
   return x>y?x:y; 
} 

inline int read()
{
    char c=getchar();int x=0;
    while(c<'0' || c>'9'){c=getchar();}
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x;
}

inline void built(int &k,int l,int r)
{
    k=++cnt;
    if(l==r) return;
    int mid=(l+r)>>1;
    built(ls[k],l,mid);
    if(r>mid) built(rs[k],mid+1,r);
}

inline void build(int k,int l,int r)
{
    built(root[k],1,S);
    if(l==r) return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    if(r>mid) build(k<<1|1,mid+1,r);    
}

inline ll fin(int k,int l,int r,int x,int y)
{
    if(l>=x && r<=y){
      return mx[k];
    } 
    int mid=(l+r)>>1;
    ll ta=0,tb=0,tc=inq[k];
    if(x<=mid) ta=fin(ls[k],l,mid,x,y);
    if(y>mid && r>mid) tb=fin(rs[k],mid+1,r,x,y);
    return max(tc,max(ta,tb));
} 

inline ll fini(int k,int l,int r,int x,int y)
{
    if(l>=x && r<=y){
        return mxx[k];
    }
    int mid=(l+r)>>1;
    ll ta=0,tb=0,tc=inqq[k];
    if(x<=mid) ta=fini(ls[k],l,mid,x,y);
    if(y>mid && r>mid) tb=fini(rs[k],mid+1,r,x,y);
    return max(tc,max(ta,tb));
}

inline ll find(int k,int l,int r,int i,int j,int x,int y)
{
    if(l>=i && r<=j){
        return fini(root[k],1,S,x,y);
    }
    int mid=(l+r)>>1;
    ll ta=0,tb=0,tc=fin(root[k],1,S,x,y);
    if(i<=mid) ta=find(k<<1,l,mid,i,j,x,y);
    if(j>mid && r>mid)  tb=find(k<<1|1,mid+1,r,i,j,x,y);
    return max(tc,max(ta,tb));
}

inline void inn(int k,int l,int r,int x,int y,ll hh)
{
    mx[k]=max(mx[k],hh);
    if(l>=x && r<=y) {inq[k]=max(inq[k],hh);return;}
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) inn(ls[k],l,mid,x,y,hh);
    if(y>mid && r>mid) inn(rs[k],mid+1,r,x,y,hh); 
}

inline void innn(int k,int l,int r,int x,int y,ll hh)
{
    mxx[k]=max(mxx[k],hh);
    if(l>=x && r<=y) {inqq[k]=max(inqq[k],hh);return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid) innn(ls[k],l,mid,x,y,hh);
    if(y>mid && r>mid) innn(rs[k],mid+1,r,x,y,hh); 
} 

inline void in(int k,int l,int r,int i,int j,int x,int y,ll hh)
{
    innn(root[k],1,S,x,y,hh);
    if(l>=i && r<=j){
        inn(root[k],1,S,x,y,hh);return;
    }
    if(l==r)return;
    int mid=(l+r)>>1;
    if(i<=mid) in(k<<1,l,mid,i,j,x,y,hh);
    if(j>mid && r>mid) in(k<<1|1,mid+1,r,i,j,x,y,hh); 
}

int main(){
    D=read()+1;S=read()+1;n=read();
    build(1,1,D);
    while(n--){
        d=read();s=read();w=read();x=read()+1;y=read()+1;
        ll t=find(1,1,D,x,x+d-1,y,y+s-1);//-1 to make sure that one is covered by another
        t+=w;
        in(1,1,D,x,x+d-1,y,y+s-1,t);//remember to -1
        ans=max(ans,t); 
    }
    printf("%lld\n",ans);
    //system("pause");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值