[POI2006]TET-Tetris 3D
要做这道题我们需要两个前置技能:二维线段树和标记永久化。
我们使用一维线段树来维护一个序列,那我们想维护一个矩阵的时候,二维线段树应运而生。
二维线段树好像有两种实现方法。一是对于每一个节点(x轴上的每个点)在里面再开一棵线段树(表示一个y轴)(这好像更多人管他叫树套树做法?)
第二个是把它变成一棵四叉树……不过这种实现方法我并没有学。
具体的实现方法其实非常优秀,可以选择写结构体。就是对于一次修改,正常在我们一维线段树上传6个参数,我们再多传两个记录y轴修改范围的参数,然后在到达制定修改位置的时候我们进入y轴去修改即可。(这个可以一会看代码)
之后再说说标记永久化,我们在进行区间修改的时候是要pushdown的,但是这个对于树套树等等比较复杂的数据结构是很麻烦的,而且尤其是像这道题,你不知道怎么下放。所以我们进行标记永久化。以区间加法为例,我们维护sum和add两个标记,在修改到指定位置之前,每经过一个区间就把他的sum加上加的值*区间长度。然后在修改到指定位置(完全覆盖的时候)我们把这个区间的add加上加的值。
然后在每次之后的查询中,我们每次向下走,都要加上这个区间所贡献的add*区间长度,这样才是结果(注意区间完全覆盖的时候不加,因为这个已经算在sum里面了)
那我们现在来看这道题。题目翻译的太好了,特别简练,而且是直接告诉你要干啥而不是让你再总结一遍。
我们用二维线段树维护这个序列。对于每次修改,我们先查询这个矩阵内的最大值,然后直接改成最大值+h的高度就行。
在途中是可以标记永久化的。这个思路和区间加法是一样的,不过这个变成了永久取最大值,所以在每次下放的时候更新当前mx(最大值),直到区间完全覆盖更新tag(修改后最大值),在query的时候对于一个区间,所有query的值都要与这个区间的tag去比较,然后返回较大值。
看一下代码。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define pr pair<int,int> #define mp make_pair #define fi first #define sc second using namespace std; typedef long long ll; const int M = 100005; const int N = 8005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int d,s,h,x,y,k,n,m; struct segy//维护y轴 { int mx[N],tag[N]; void change(int p,int l,int r,int kl,int kr,int val) { mx[p] = max(mx[p],val);//更新(标记永久化) if(l == kl && r == kr) { tag[p] = max(tag[p],val);//区间完全覆盖的时候更新 return; } int mid = (l+r) >> 1;//一切正常 if(kr <= mid) change(p<<1,l,mid,kl,kr,val); else if(kl > mid) change(p<<1|1,mid+1,r,kl,kr,val); else change(p<<1,l,mid,kl,mid,val),change(p<<1|1,mid+1,r,mid+1,kr,val); } int query(int p,int l,int r,int kl,int kr) { if(l == kl && r == kr) return mx[p];//完全覆盖 int cur = tag[p],mid = (l+r) >> 1;//剩下的所有与tag比较 if(kr <= mid) cur = max(query(p<<1,l,mid,kl,kr),cur); else if(kl > mid) cur = max(query(p<<1|1,mid+1,r,kl,kr),cur); else cur = max(cur,max(query(p<<1,l,mid,kl,mid),query(p<<1|1,mid+1,r,mid+1,kr))); return cur; } }; struct segx//维护x轴 { segy mx[N],tag[N]; void change(int p,int l,int r,int kl,int kr,int zl,int zr,int val) { mx[p].change(1,1,m,zl,zr,val);//每次都修改(标记永久化) if(l == kl && r == kr) { tag[p].change(1,1,m,zl,zr,val);//只有完全覆盖才修改 return; } int mid = (l+r) >> 1;//剩下的很正常 if(kr <= mid) change(p<<1,l,mid,kl,kr,zl,zr,val); else if(kl > mid) change(p<<1|1,mid+1,r,kl,kr,zl,zr,val); else change(p<<1,l,mid,kl,mid,zl,zr,val),change(p<<1|1,mid+1,r,mid+1,kr,zl,zr,val); } int query(int p,int l,int r,int kl,int kr,int zl,int zr) { if(l == kl && r == kr) return mx[p].query(1,1,m,zl,zr); int cur = tag[p].query(1,1,m,zl,zr),mid = (l+r) >> 1; if(kr <= mid) cur = max(cur,query(p<<1,l,mid,kl,kr,zl,zr)); else if(kl > mid) cur = max(cur,query(p<<1|1,mid+1,r,kl,kr,zl,zr)); else cur = max(cur,max(query(p<<1,l,mid,kl,mid,zl,zr),query(p<<1|1,mid+1,r,mid+1,kr,zl,zr))); return cur;//这里每次在修改的时候都要与tag比较,完全覆盖的时候不用,因为已经更新过了 } }t; int main() { n = read(),m = read(),k = read(); while(k--) { d = read(),s = read(),h = read(),x = read(),y = read(); int g = t.query(1,1,n,x+1,x+d,y+1,y+s);//查询最大值 t.change(1,1,n,x+1,x+d,y+1,y+s,g+h);//区间修改 } printf("%d\n",t.query(1,1,n,1,n,1,m)); return 0; }