1. 程式人生 > >線段樹掃描線(一) 矩形面積 以hdu 1542為例

線段樹掃描線(一) 矩形面積 以hdu 1542為例

建議 傳參 ret 思路 n) 使用 十分 mes ati

還是老規矩,傳送門 hdu 1542

不做過多解釋了,就是給出n個矩形,求出這些矩形所覆蓋的面積和。由於n很小,因而這道題不是必須用線段樹

先想想怎麽辦,先來一個例圖(稍微有點復雜)

技術分享圖片

根據數學知識,我們可以像這樣:

技術分享圖片

將它用紅線分割,然後維護每條紅線被矩形覆蓋的部分的長度,然後乘以兩條紅線之間的距離,最後累加每一部分的面積而得到答案,沒什麽問題吧。

接下來,問題轉化為維護線段的長度了,於是我們考慮線段樹,但我們發現了一個重大問題:x坐標是實數,不能直接用線段樹維護,但我們不虛,把浮點離散成整數不就好了嗎。

然後,重中之重來了,線段樹裏存什麽:

(1):存節點是否被覆蓋:用腦子思考一下,就會發現這不對:例如,離散後最大坐標為6,插入一條線段[2,6],那麽3,4之間的一段就消失了,必然WA,就算用強大的碼力解決了這個問題,代碼也必然會十分惡心,因此,我們不要這樣存儲

(2):存一段線是否被覆蓋:這是正確的,但鑒於鄙人語文不太好,難以用語言解釋,上一張圖:

例如,離散後最大坐標為6,則我們要這樣:

技術分享圖片

然而,我為了代碼好寫,傳參數時傳的依然是離散後的點坐標,因此,dis(L,R)=val[R+1]-val[L],

接下來,就是對線段樹進行改造,使之能正確維護線段長度

我們可以這樣考慮,在矩形下側的邊進入時,加一,而在上側的邊進入時,加負一,然後,對線段樹進行修改,每一個節點中記錄這個區間被完全覆蓋的次數和被覆蓋的長度,

求解前,對2n條邊按高度排序,從最低的邊開始,逐個枚舉到最高的一條邊,累加答案即可。

大體的思路有了,但我還要多說兩句:

1.輸出時兩組之間有空行,每組的兩行之間沒有空行,正確的輸出語句應該像下面這樣

printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas,ans);

其中:cas記錄這是第幾組數據,ans為這組數據的結果

2.這道題不建議大家使用萬能頭文件,因為y1在cmath中是一個函數,這樣做會導致CE,當然,強烈不建議手欠打上

#include<cmath>

習慣不用y1的神犇們請忽略此條建議。

好了,奉上代碼

  1 #include<cstdio>
  2 #include<map>
  3 #include<algorithm>
  4 #include<cstring>
  5
using namespace std; 6 map<double,int> mp; 7 double ls[5000],x1,x2,_y1,y2; 8 int n,cnt,cd; 9 struct edge 10 { 11 int l,r,val; 12 double ll,rr,h; 13 }eds[2500]; 14 struct node 15 { 16 int cov; 17 double dis; 18 }tre[2500]; 19 int cmp(edge a,edge b){return a.h<b.h;} 20 void init() 21 { 22 cnt=1; 23 cd=1; 24 mp.clear(); 25 memset(ls,0,sizeof(ls)); 26 //由於每次樹都會被正好加成0,不用對樹進行重置 27 } 28 int fd(double tar) 29 { 30 int l=1,r=cd; 31 while(l<r) 32 { 33 int mid=(l+r)/2; 34 if(ls[mid]<tar) 35 { 36 l=mid+1; 37 } 38 else 39 { 40 r=mid; 41 } 42 } 43 return l; 44 } 45 void pushup(int L,int R,int nc) 46 { 47 if(tre[nc].cov) 48 { 49 tre[nc].dis=ls[R+1]-ls[L]; 50 } 51 else 52 { 53 if(L==R) 54 { 55 tre[nc].dis=0; 56 } 57 else 58 { 59 tre[nc].dis=tre[2*nc].dis+tre[2*nc+1].dis; 60 } 61 } 62 } 63 void ins(int l,int r,int dt,int nc,int L,int R) 64 { 65 if(l<=L&&r>=R) 66 { 67 tre[nc].cov+=dt; 68 if(tre[nc].cov) 69 { 70 tre[nc].dis=ls[R+1]-ls[L]; 71 } 72 else 73 { 74 if(L==R) 75 { 76 tre[nc].dis=0; 77 } 78 else 79 { 80 tre[nc].dis=tre[2*nc].dis+tre[2*nc+1].dis; 81 } 82 } 83 return; 84 } 85 int mid=(L+R)/2; 86 if(l<=mid) 87 { 88 ins(l,r,dt,2*nc,L,mid); 89 } 90 if(r>mid) 91 { 92 ins(l,r,dt,2*nc+1,mid+1,R); 93 } 94 pushup(L,R,nc); 95 } 96 int main() 97 { 98 int cas=1; 99 while(1) 100 { 101 scanf("%d",&n); 102 if(n==0) 103 { 104 return 0; 105 } 106 init(); 107 for(int i=1;i<=n;i++) 108 { 109 scanf("%lf%lf%lf%lf",&x1,&_y1,&x2,&y2); 110 eds[cnt].h=_y1; 111 eds[cnt].ll=x1; 112 eds[cnt].rr=x2; 113 eds[cnt].val=1; 114 cnt++; 115 eds[cnt].h=y2; 116 eds[cnt].ll=x1; 117 eds[cnt].rr=x2; 118 eds[cnt].val=-1; 119 cnt++; 120 if(!mp[x1]) 121 { 122 mp[x1]=1; 123 ls[cd]=x1; 124 cd++; 125 } 126 if(!mp[x2]) 127 { 128 mp[x2]=1; 129 ls[cd]=x2; 130 cd++; 131 } 132 } 133 sort(ls+1,ls+cd); 134 for(int i=1;i<cnt;i++) 135 { 136 eds[i].l=fd(eds[i].ll); 137 eds[i].r=fd(eds[i].rr); 138 }//從讀入到這裏都是離散化 139 sort(eds+1,eds+cnt,cmp); 140 double last=eds[1].h,ans=0; 141 ins(eds[1].l,eds[1].r-1,eds[1].val,1,1,cd-1); 142 for(int i=2;i<cnt;i++) 143 { 144 ans+=(eds[i].h-last)*tre[1].dis; 145 ins(eds[i].l,eds[i].r-1,eds[i].val,1,1,cd-1); 146 last=eds[i].h; 147 } 148 printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas,ans); 149 cas++; 150 } 151 }

線段樹掃描線(一) 矩形面積 以hdu 1542為例