1. 程式人生 > >線段樹、掃描線、離散化求面積並(hdu1542)

線段樹、掃描線、離散化求面積並(hdu1542)

題目連結:
https://vjudge.net/problem/HDU-1542
大牛部落格連結:http://blog.csdn.net/u013480600/article/details/22548393
講解的很生動。
分析:
首先我們將矩形的上下邊分為上位邊(即y座標大的那條平行於x軸的邊),和下位邊(y座標小的平行於x軸的邊).然後我們把所有矩形的上下位邊按照他們y座標從小到大排序;
需要把x座標離散化,這樣才能用線段樹來維護資訊.所謂離散化,就是將元素排序,去重,這樣得到一個數組a,這個陣列就是建立線段樹的關鍵;
主要知識:
關於線段樹:線段樹的葉節點([l,r],這裡l==r,指的是建立線段樹陣列中的下標),線段樹中葉節點代表的都是某一個數組的下標

,其他節點則是多個下標。
建立一個線段樹必須要有一個數組,根據其座標建立線段樹。本題中葉節點控制的[l,l]實際上代表的是[ a[l],a[l+1] ].線段樹中其他節點控制的區間[L,R],也是指的x座標軸的第L個區間到第R個區間的範圍,也就是X[L]到X[R+1]座標的範圍.
關於離散化:
離散化就是壓縮區間,使原有的長區間對映到新的短區間,但是區間壓縮前後的覆蓋關係不變,本題中離散化就是把矩形端點的x座標放進一個數組中,去掉重複的座標。,這就用到了unique函式。
關於unique函式:
unique()函式是一個去重函式,STL中unique的函式 unique的功能是去除相鄰的重複元素(只保留一個),還有一個容易忽視的特性是它並不真正把重複的元素刪除。之所以說比不真正把重複的元素刪除,其實是,該函式把重複的元素一到後面去了,然後依然儲存到了原陣列中
返回值是相互間不相同的個數,注意用這個函式之前需要排序。
程式碼如下:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
//掃描線,也就是矩形的和x軸平行的那些邊,記錄高度和左右端點座標,和
class Seg
{
   public:
        double x1,x2,h;
        int
flag; Seg(double a=0,double b=0,double c=0,double d=0):x1(a),x2(b),h(c),flag(d){} }; bool cmp(Seg p,Seg q) { return p.h<q.h||(p.h==q.h&&p.flag>q.flag); } const int maxn=100+5; Seg seg[maxn<<1]; double cnt[maxn<<3],sum[maxn<<3];//cnt表示當前區間狀態,0表示被覆蓋,大於零則被掃描,sum代表區間長度 double x[maxn<<1]; //建樹, void build(int l,int r,int rt) { cnt[rt]=sum[rt]=0; if(l==r)return; int m=(l+r)>>1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); } //注意[l,r]間的長度是x[r+1]-x[l] void push_up(int l,int r,int rt) { if(cnt[rt])sum[rt]=x[r+1]-x[l]; else if(l==r)sum[rt]=0; else sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } //更新,如果是上位邊則加一,下位邊減一 void update(int ll,int rr,int c,int l,int r,int rt) { if(ll<=l&&rr>=r) { cnt[rt]+=c; push_up(l,r,rt); return ; } int m=(l+r)>>1; if(ll<=m)update(ll,rr,c,l,m,rt<<1); if(rr>m)update(ll,rr,c,m+1,r,rt<<1|1); push_up(l,r,rt); } int main() { int n; int kase=0; while(scanf("%d",&n)!=EOF&&n) { int nums=0,numx=0; for(int i=0;i<n;i++) { double a,b,c,d; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); seg[nums++]=Seg(a,c,b,1); seg[nums++]=Seg(a,c,d,-1); x[numx++]=a; x[numx++]=c; } build(0,numx-2,1); sort(x,x+numx); sort(seg,seg+nums,cmp); numx=unique(x,x+numx)-x; //離散化 double res=0; //讓每一條掃描線掃描 for(int i=0;i<nums-1;i++) { int ll=lower_bound(x,x+numx,seg[i].x1)-x; int rr=lower_bound(x,x+numx,seg[i].x2)-x; update(ll,rr-1,seg[i].flag,0,numx-2,1); res+=sum[1]*(seg[i+1].h-seg[i].h); } printf("Test case #%d\n",++kase); printf("Total explored area: %.2lf\n\n",res); } }