離散化-線段樹-掃描線小結
昨天打了南寧賽區的網路賽,碰到一個求矩形並的面積的模板題,然而沒A出來(不會啊)。以前也碰到過類似線段樹+掃描線的題目,只能怪自己以前沒補題吧哎~。
關於這個知識點,網上隨便一搜就能搜到大堆,就說一下我在學習的過程中碰到的問題。
一:首先是離散化,定義就不說了(其實不知道2333),我籠統地理解就是把大的資料範圍縮小,同時又保證資料之間地關係不變,比如說座標的大小,如果給你一些點,你需要的屬性只是點與點之間的距離(與各個點的具體座標值無關),那麼我們可以把點的座標對映到一個比較小的範圍,然後把原來的屬性放到新的座標上就好,一個最簡單的例子:對於點(-1e8,0),(1e8,0)之間的距離,如果要求的是兩個點之間的水平距離,那麼我們把-1e8對映到1,1e8對映到2,那麼距離就是1到2的距離。這只是一個很籠統的說法,總之我理解的就是把很多點壓縮到一起,同時記錄點與點之間的屬性,當你詢問點與點之間的關係時,就可以直接通過查詢新的點得到。
迴歸主題,這裡的離散化的作用也是化大座標為小座標(我這裡自下而上掃),方便建樹,比如原來最大範圍-10000~10000的線段,而兩個值對應的對映為1~100,那麼我們就只要建立[1~100]的線段樹,對於每一個區間的實際長度,再根據左右兩個端點對應的值計算。那麼如何保證能通過對映能相互查詢到呢--把原資料排序並去重,然後記每一個數的對映值就是下標,那麼我們當要知道某個原資料的對映值時,只要判斷它在第幾個元素,反過來就更加簡單,直接下標詢問。為什麼要去重?--我們需要的的是值的大小的對映值,相同的值的對映應該一樣,故重複的資料可以去掉,這裡的離散化,我大概就這麼理解的。
二:線段樹,線段樹維護的是總區間的覆蓋長度嘛,開始我一直有一段程式碼看不懂:if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l];就是當一個區間被覆蓋時,這裡的區間又端點要對應加一後的原來的值--因為插入l~r的線段的時候,線段樹上更新的是l~r-1區間,但是為什麼要這麼寫,直接更新l~r區間,然後每一個區間的長度就是左右端點的差不行嗎?後來看到一句值得話:每個節點應該對應一個區間(不然葉子節點維護的是什麼,仔細想一想會發現更新的時候會出現問題的)。所以對於l~r區間的線段,我們用線段樹的l~r-1區間去維護(葉子節點的區間長度為1了)。
三:後面的問題就不大了,掃描線只是個虛名罷了,就是把所有的邊排一下序,然後順序掃一遍。
也貼個程式碼念一些吧:
#include<iostream> #include<cstring> #include<cstdio> #include<string> #include<algorithm> #define LL long long using namespace std; const int maxn=1010; struct Edge{ int lx,rx,h; int val; }edge[maxn<<1]; struct node{ int l,r; int is_cover,len; int mid(){return (l+r)>>1;} }tree[maxn<<3]; bool cmp(Edge e1,Edge e2){ return e1.h<e2.h; } int Pre_x[maxn<<1]; int Now_x[maxn<<1]; void Build_tree(int i,int l,int r){ tree[i].l=l,tree[i].r=r; tree[i].is_cover=0;tree[i].len=0; if(l==r)return; int m=tree[i].mid(); Build_tree(i<<1,l,m); Build_tree(i<<1|1,m+1,r); } void Get_len(int i){ if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l]; else if(tree[i].l==tree[i].r)tree[i].len=0; else{tree[i].len=tree[i<<1].len+tree[i<<1|1].len;} } void Update_tree(int i,int l,int r,int val){ if(tree[i].l==l&&tree[i].r==r){ tree[i].is_cover+=val; Get_len(i); return; } int m=tree[i].mid(); if(r<=m)Update_tree(i<<1,l,r,val); else if(l>m)Update_tree(i<<1|1,l,r,val); else{ Update_tree(i<<1,l,m,val); Update_tree(i<<1|1,m+1,r,val); } Get_len(i); } int main(){ freopen("in.txt","r",stdin); int n,x1,y1,x2,y2; while(~scanf("%d",&n)&&n){ for(int i=0;i<n;i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); edge[i<<1].lx=edge[i<<1|1].lx=x1; edge[i<<1].rx=edge[i<<1|1].rx=x2; edge[i<<1].h=y1;edge[i<<1].val=1; edge[i<<1|1].h=y2;edge[i<<1|1].val=-1; Pre_x[i<<1]=x1;Pre_x[i<<1|1]=x2; } sort(edge,edge+2*n,cmp); sort(Pre_x,Pre_x+2*n); int cnt=1;Now_x[0]=Pre_x[0]; for(int i=1;i<2*n;i++){ if(Pre_x[i]!=Pre_x[i-1]){ Now_x[cnt++]=Pre_x[i]; } } Build_tree(1,0,cnt-2); LL ans=0; for(int i=0;i<2*n;i++){ int l=lower_bound(Now_x,Now_x+cnt,edge[i].lx)-Now_x; int r=lower_bound(Now_x,Now_x+cnt,edge[i].rx)-Now_x-1; Update_tree(1,l,r,edge[i].val); ans+=(LL)(edge[i+1].h-edge[i].h)*tree[1].len; } printf("%lld\n",ans); } printf("*\n"); return 0; }
題目連結:點選開啟連結
雖然搞了比較久,但是看到AC的那一刻還是很開心的。