bzoj1513 [POI2006]Tet-Tetris 3D(二维线段树)

Description

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

Input

第一行给出三个整数 D, S and N ( 1<= N<= 20 000, 1<= D, S <=1 000), 分别表示平板的长和宽以及下落立方体的数目. 接下来N 行每行描述一个立方体.
每行包含5个整数: 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).

Output

一个整数表示所有立方体落下后最高的方块的高度.

Sample Input

7 5 4

4 3 2 0 0

3 3 1 3 0

7 1 2 0 3

2 3 3 2 2

Sample Output

6

[ Submit][ Status][ Discuss]


分析:
一眼口hu二维线段树,然而并写不出来
发现以前学的二维线段树都是单点修改,区间查询,非常naive

区间修改,区间查询
我们会发现两个没法解决的问题:
1.标记的下传
2.信息的上传

也想过标记永久化,然而思想不是很成熟

对于标记我们没有办法很好的下传
所以直接标记永久化,查询的时候对线段树路径上的每一个点都进行询问

而信息上传我们也没有什么好方法
那么不上传不就好了!
由于修改只会使元素的值增大,因此区间内只要有任意一个位置被更新过,那么就会对询问的答案产生影响
因此只需要修改的时候对线段树路径上的每一个点都修改一下就好了

我们需要维护两个标记:A标记和B标记
A标记在修改的时候精确覆盖到线段树上的每个对应节点上,查询的时候精准定位
B标记记录的是线段树一条链上的标记,标记永久化

A标记是精准标记,B标记是标记永久化的产物
但是在查询的时候,因为是标记永久化,所以B标记反而是准确的,但是我们也不能忽略A标记(顺带便就查询了吧)

tip

真·神奇

看好了数组啊

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=4005;
int D,S,n;
int xa,xb,ya,yb,ans;
struct node_y{
    int a[N],b[N];

    void change(int bh,int l,int r,int L,int R,int z) {
        b[bh]=max(b[bh],z);
        if (l>=L&&r<=R) {
            a[bh]=max(a[bh],z);
            return;
        }
        int mid=(l+r)>>1;
        if (L<=mid) change(bh<<1,l,mid,L,R,z);
        if (R>mid) change(bh<<1|1,mid+1,r,L,R,z);
    }

    int ask(int bh,int l,int r,int L,int R) {
        if (l>=L&&r<=R) return b[bh];
        int mid=(l+r)>>1;
        int ans=a[bh];
        if (L<=mid) ans=max(ans,ask(bh<<1,l,mid,L,R));
        if (R>mid) ans=max(ans,ask(bh<<1|1,mid+1,r,L,R));
        return ans; 
    }
};
struct node_x{
    node_y a[N],b[N];

    void change(int bh,int l,int r,int L,int R,int z) {
        b[bh].change(1,1,S,ya,yb,z);
        if (l>=L&&r<=R) {
            a[bh].change(1,1,S,ya,yb,z);
            return;
        }
        int mid=(l+r)>>1;
        if (L<=mid) change(bh<<1,l,mid,L,R,z);
        if (R>mid) change(bh<<1|1,mid+1,r,L,R,z);
    }

    int ask(int bh,int l,int r,int L,int R) {
        if (l>=L&&r<=R) return b[bh].ask(1,1,S,ya,yb);    //准确询问 
        int mid=(l+r)>>1;
        int ans=a[bh].ask(1,1,S,ya,yb);                   //标记永久化询问 
        if (L<=mid) ans=max(ans,ask(bh<<1,l,mid,L,R));
        if (R>mid) ans=max(ans,ask(bh<<1|1,mid+1,r,L,R));
        return ans;
    }
};
node_x T;

int main()
{
    scanf("%d%d%d",&D,&S,&n);
    int d,s,h,x,y;
    for (int i=1;i<=n;i++) {
        scanf("%d%d%d%d%d",&d,&s,&h,&x,&y);
        xa=x+1; xb=x+d; ya=y+1; yb=y+s;
        int o=T.ask(1,1,D,xa,xb);
        T.change(1,1,D,xa,xb,o+h);
    }
    ya=1,yb=S;
    printf("%d",T.ask(1,1,D,1,D));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值