線段樹、掃描線、離散化求面積並(hdu1542)
阿新 • • 發佈:2019-02-09
題目連結:
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);
}
}