[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> #definemp 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; }
[POI2006]TET-Tetris 3D