国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

線段樹標記永久化個人理解 & BZOJ 1513 [POI2006]Tet-Tetris 3D

2019-11-08 19:49:01
字體:
來源:轉載
供稿:網友

Description Task: Tetris 3D “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


將x與y看作一個二維平面

每個立方體的下落可以看作查詢矩陣 (x+1,y+1) (x+d,y+s) 內的最大值并將矩陣內所有值變為maxh+w

考慮樹套樹(二維線段樹)的做法 我們發現標記下傳(pushdown)與信息上傳(pushup)很難辦到

這時標記永久化派上用場了

那標記永久化是個啥呢?

在寫普通線段樹時 我們對修改的區間打上標記 并在查詢的時候下放標記并更新答案

那么我們是否可以不對標記進行下放 在路過該節點(需要用到它或它兒子的信息)的時候把修改對答案的影響加上 來省去標記下放的過程呢?

吼哇!

于是我們yy了一個新的不用pushdown的線段樹

新線段樹的每個節點維護 sum 與 all 兩個標記

分別代表 區間和 與 節點所表示的區間都被加了多少

sum也可以看作該節點所表示的區間內有一些地方被加上了一些值 它們的和為sum(具體位置我們不關心)

修改操作就相當于一路更新所經過節點的sum值 直到目標區間與當前結點區間完全覆蓋 更新該點的all值

查詢操作與修改操作正好相反 計算所經過節點的all值對答案的貢獻 直到目標區間與當前結點區間完全覆蓋 并用該節點的sum值更新答案

上幾個圖理解一下吧:

標記永久化1 標記永久化2 標記永久化3 標記永久化4

/* 區間修改 區間求和 l,r為當前節點表示的區間 nl,nr為目標區間*/#define LL long long#define ls (cnt<<1)#define rs (cnt<<1|1)LL n,m;LL sum[MAXN*4],all[MAXN*4],w[MAXN];void build(LL l,LL r,LL cnt){ if(l==r){sum[cnt]=w[l];return;} LL mid=(l+r)>>1; build(l,mid,ls),build(mid+1,r,rs); sum[cnt]=sum[ls]+sum[rs];}LL query(LL l,LL r,LL nl,LL nr,LL cnt){ LL add=(nr-nl+1)*all[cnt],mid=(l+r)>>1;//計算標記對答案的影響 if(l==nl&&r==nr) return add+sum[cnt]; if(nr<=mid) return add+query(l,mid,nl,nr,ls); if(nl>=mid+1) return add+query(mid+1,r,nl,nr,rs); return add+query(l,mid,nl,mid,ls)+query(mid+1,r,mid+1,nr,rs);}void update(LL l,LL r,LL nl,LL nr,LL v,LL cnt){ if(l==nl&&r==nr){all[cnt]+=v;return;}//當前結點表示的區間與目標區間完全重合 更新標記(all)值 LL mid=(l+r)>>1; if(nr<=mid) update(l,mid,nl,nr,v,ls); else if(nl>=mid+1) update(mid+1,r,nl,nr,v,rs); else update(l,mid,nl,mid,v,ls),update(mid+1,r,mid+1,nr,v,rs); sum[cnt]+=(nr-nl+1)*v;//更新區間sum}int main(){ n=read(),m=read(); for(LL i=1;i<=n;i++) w[i]=read(); build(1,n,1); while(m--) { LL t=read(),a=read(),b=read(),c; if(t==1) c=read(),update(1,n,a,b,c,1); else 相當于我們用all標記來代替了標記下傳的過程 因為當查詢到該節點及以下節點時 必定會用該節點的all值來更新答案

sum則代替了信息上傳的過程 維護該節點所表示區間的信息 而信息上傳已經在我們進行修改操作的時候順便做了

因此不再需要信息上傳(pushup)與標記下傳(pushdown)兩個操作

回到本題 對于第一維 我們也可以維護這樣的兩個值 all 與 have

分別代表 該區間全變成了all

與該區間內有一些點變為了have

然后第二維用 普通 or 標記永久化 線段樹來維護這兩個值即可。

#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#define MAXN 2505#define LL long longusing namespace std;int read(){ int f=1,t=0; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();} return t*f;}/*----- head -----*/#define ls (cnt<<1)#define rs ((cnt<<1)+1)#define mid ((l+r)>>1)int n,m,p;struct trey{ int all[MAXN],have[MAXN]; void update(int l,int r,int y1,int y2,int v,int cnt) { if(l==y1&&r==y2){all[cnt]=max(all[cnt],v);return;} have[cnt]=max(have[cnt],v); if(y2<=mid) update(l,mid,y1,y2,v,ls); else if(y1>mid) update(mid+1,r,y1,y2,v,rs); else update(l,mid,y1,mid,v,ls),update(mid+1,r,mid+1,y2,v,rs); } int query(int l,int r,int y1,int y2,int cnt) { int ans=all[cnt]; if(l==y1&&r==y2) return max(ans,have[cnt]); if(y2<=mid) return max(ans,query(l,mid,y1,y2,ls)); if(y1>mid) return max(ans,query(mid+1,r,y1,y2,rs)); ans=max(ans,query(l,mid,y1,mid,ls)); ans=max(ans,query(mid+1,r,mid+1,y2,rs)); return ans; }};struct trex{ trey have[MAXN],all[MAXN]; void update(int l,int r,int x1,int x2,int y1,int y2,int v,int cnt) { if(l==x1&&r==x2){all[cnt].update(1,m,y1,y2,v,1);return;} have[cnt].update(1,m,y1,y2,v,1); if(x2<=mid) update(l,mid,x1,x2,y1,y2,v,ls); else if(x1>mid) update(mid+1,r,x1,x2,y1,y2,v,rs); else update(l,mid,x1,mid,y1,y2,v,ls),update(mid+1,r,mid+1,x2,y1,y2,v,rs); } int query(int l,int r,int x1,int x2,int y1,int y2,int cnt) { int ans=all[cnt].query(1,m,y1,y2,1); if(l==x1&&r==x2) return max(ans,have[cnt].query(1,m,y1,y2,1)); if(x2<=mid) return max(ans,query(l,mid,x1,x2,y1,y2,ls)); if(x1>mid) return max(ans,query(mid+1,r,x1,x2,y1,y2,rs)); ans=max(ans,query(l,mid,x1,mid,y1,y2,ls)); ans=max(ans,query(mid+1,r,mid+1,x2,y1,y2,rs)); return ans; }}T;int main(){ n=read(),m=read(),p=read(); while(p--) { int d=read(),s=read(),w=read(),x=read(),y=read(),maxh; maxh=T.query(1,n,x+1,x+d,y+1,y+s,1); T.update(1,n,x+1,x+d,y+1,y+s,maxh+w,1); } printf("%d/n",T.query(1,n,1,n,1,m,1)); return 0;}

注意本題矩形內的最大值只會變的越來越大 所以信息上傳時不會用到左右兒子的信息 使用標記永久化就不會出現問題

若答案并不是單調遞增的

那么我們在更新答案的時候就會出現問題 因為我們并不知道標記打上的順序是誰在前誰在后


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 台南市| 宁波市| 桐乡市| 保德县| 特克斯县| 额敏县| 鲜城| 微博| 合山市| 庆阳市| 临漳县| 永福县| 崇仁县| 遂昌县| 丰台区| 安远县| 桃园县| 贵溪市| 芦山县| 阳曲县| 鹤峰县| 介休市| 拜泉县| 楚雄市| 洪洞县| 凤城市| 湖州市| 霍林郭勒市| 巧家县| 祁连县| 杨浦区| 肥乡县| 陆川县| 临沭县| 凌海市| 郑州市| 思茅市| 万源市| 孟村| 新蔡县| 沅陵县|