二维线段树
例题
P3437 [POI2006]TET-Tetris 3D
最近,有人发明了一种三维版的俄罗斯方块。和二维版本类似,一些立方体按照一定的顺序掉落,直到碰到别的方块或是地面才会停止掉落。立方体停止掉落后会一直保持掉落时的位置,直到游戏结束。
你的朋友决定以这个新版本的俄罗斯方块为背景,出一道题。给出每个立方体的掉落顺序和其掉落的轨迹,在所有方块完成掉落后求出最高方块的高度。在这个游戏中,方块均垂直下落,且方块不会旋转或翻转。为了方便描述,我们会建立一个空间直角坐标系,该坐标系的原点为地面的一角,并且坐标轴与地面边缘平行。
现在轮到你解决这个问题了。
输入格式
第一行三个整数 D,S,ND,S,ND,S,N,分别为地面的长度,宽度,和将要掉落的立方体数量。
接下来 N 行,每行五个整数 \(d_i,s_i,w_i,x_i,y_i\),描述一个掉落的立方体。其中 \(d_i,s_i,w_i\) 分别代表立方体的长,宽,高。立方体的底面(即长 \(\times\) 宽的那一面)将正对地面。立方体底面四个角在地面的投影坐标分别为 \((x_i,y_i),(x_i+d_i,y_i),(x_i,y_i+s_i),(x_i+d_i,y_i+s_i)\)。
输出格式
输出一个整数,即方块掉落结束后最高方块的高度。
数据范围
\(1 \leq N \leq 20000,1 \leq D,S \leq 1000,d_i,s_i \geq 1, 1 \leq w_i \leq 100000,0 \leq x_i,d_i+x_i \leq D, 0 \leq y_i,s_i+y_i \leq S\)。
解析
线段树套线段树。
对于内层线段树,每个节点存两个值 mx、tag。mx 代表当前区间的最大值,tag 是永久化标记,表示对整个区间所做的修改,对于所有的子区间都有效。
对于外层线段树,每个节点开两个内层线段树mx、tag,定义同上。
其实这题最好动态开点。
代码
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN = 1e3 + 500;
int d, s, n;
struct segn {
int mx[MAXN << 1], tag[MAXN << 1];
void modify(int now, int l, int r, int lf, int rg, int val) {
mx[now] = max(mx[now], val);
if(l == lf && r == rg) {
tag[now] = max(tag[now], val);
return ;
}
int mid = (l + r) >> 1;
if(rg <= mid) modify(now << 1, l, mid, lf, rg, val);
else if(lf > mid) modify(now << 1 | 1, mid + 1, r, lf, rg, val);
else modify(now << 1, l, mid, lf, mid, val), modify(now << 1 | 1, mid + 1, r, mid + 1, rg, val);
}
int query(int now, int l, int r, int lf, int rg) {
if(l == lf && r == rg) return mx[now];
int mid = (l + r) >> 1, res = tag[now];
if(rg <= mid) res = max(res, query(now << 1, l, mid, lf, rg));
else if(lf > mid) res = max(res, query(now << 1 | 1, mid + 1, r, lf, rg));
else {
res = max(res, query(now << 1, l, mid, lf, mid));
res = max(res, query(now << 1 | 1, mid + 1, r, mid + 1, rg));
}
return res;
}
};
struct segw {
segn mx, tag;
}tr[MAXN << 1];
void change(int now, int lf, int rg, int l, int r, int ll, int rr, int val) {
tr[now].mx.modify(1, 1, s, ll, rr, val);
if(lf == l && rg == r) return tr[now].tag.modify(1, 1, s, ll, rr, val), void();
int mid = (lf + rg) >> 1;
if(r <= mid) change(now << 1, lf, mid, l, r, ll, rr, val);
else if(l > mid) change(now << 1 | 1, mid + 1, rg, l, r, ll, rr, val);
else {
change(now << 1, lf, mid, l, mid, ll, rr, val);
change(now << 1 | 1, mid + 1, rg, mid + 1, r, ll, rr, val);
}
}
int getval(int now, int lf, int rg, int l, int r, int ll, int rr) {
if(lf == l && rg == r) return tr[now].mx.query(1, 1, s, ll, rr);
int mid = (lf + rg) >> 1, res = tr[now].tag.query(1, 1, s, ll, rr);
if(r <= mid) res = max(res, getval(now << 1, lf, mid, l, r, ll, rr));
else if(l > mid) res = max(res, getval(now << 1 | 1, mid + 1, rg, l, r, ll, rr));
else {
res = max(res, getval(now << 1, lf, mid, l, mid, ll, rr));
res = max(res, getval(now << 1 | 1, mid + 1, rg, mid + 1, r, ll, rr));
}
return res;
}
int main() {
scanf("%d%d%d",&d, &s, &n);
int a, b, c, x, y;
for(int i = 1; i <= n; i++) {
scanf("%d%d%d%d%d",&a, &b, &c, &x, &y);
++x; ++y;
int h = getval(1, 1, d, x, x + a - 1, y, y + b - 1);
change(1, 1, d, x, x + a - 1, y, y + b - 1, h + c);
}
printf("%d\n",getval(1, 1, d, 1, d, 1, s));
return 0;
}