[POJ1151][HDU1542]Atlantis(线段树,扫描线)

英文题面,我就只放个传送门了。

Solution

   题意是算矩形面积并,这是扫描线算法能解决的经典问题。

  算法的大致思想是,把每一个矩形拆成上边下边(以下称作扫描线),每条扫描线有四个参数l,r,h,v。l和r为它的左右端点的横坐标,h为扫描线的纵坐标,v下面再解释。

  然后把扫描线按h从小到大排序,想一想,所有相邻扫描线之间的有效面积(即被矩形覆盖的面积)加起来是不是就是ans?

  怎么求呢?我们从下往上处理,设当前处理到第i条扫描线,设第i条扫描线与第i+1条扫描线之间的有效面积为s,那么s=(h[i+1]-h[i])*此时x轴被覆盖的长度。

  考虑用线段树来维护这个“x轴被覆盖的长度“,处理到第i条扫描线时,就把区间[l[i],r[i]]覆盖一次。但是这个覆盖是有时限的,当扫到某一条上边时,它对应的下边所产生的覆盖就应该被消去。

  为了方便地处理,我们把下边的v值设为1,上边的v值设为-1,这样修改时直接把区间[l[i],r[i]]的覆盖次数加v就好了。

  具体实现时,x坐标要离散化,用线段树中cnt来表示区间被覆盖的次数,sum来表示区间(当然都是在x轴上)内的覆盖长度。

  注意两点:

  1. 线段树上一个叶子节点i实际上表示的是x轴上[i,i+1]这一段,因此线段树只需要n-1个叶子节点。
  2. 由于我们只需要查询sum[1],所以update找到需修改的区间可以直接pushup,并且不用pushdown。

Code

#include
#include
#include
using namespace std;
const int N=220;
struct line{
    double l,r,h;int v;
    line(){}
    line(double a,double b,double c,int d):l(a),r(b),h(c),v(d){}
    bool operator < (const line &tmp)const{return h<tmp.h;}
}L[N];
int cnt[N<<2];
double X[N],sum[N<<2];
#define tl id<<1
#define tr id<<1|1
#define mid ((l+r)>>1)
#define lson tl,l,mid
#define rson tr,mid+1,r
void pushup(int id,int l,int r){
    if(cnt[id]) sum[id]=X[r+1]-X[l];
    else if(l==r) sum[id]=0;
    else sum[id]=sum[tl]+sum[tr];
}
void update(int id,int l,int r,int ll,int rr,int v){
    if(ll<=l&&r<=rr){
        cnt[id]+=v;
        pushup(id,l,r);
        return ;
    }
    if(ll>mid) update(rson,ll,rr,v);
    else if(rr<=mid) update(lson,ll,rr,v);
    else update(lson,ll,rr,v),update(rson,ll,rr,v);
    pushup(id,l,r);
}
int n,m,cas;
int main(){
    while(~scanf("%d",&n)&&n){
        m=0;memset(cnt,0,sizeof cnt);memset(sum,0,sizeof sum);
        for(int i=0;ii){
            double a,b,c,d;
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            L[m]=line(a,c,b,1);
            X[m++]=a;
            L[m]=line(a,c,d,-1);
            X[m++]=c;
        } 
        sort(&L[0],&L[m]);
        sort(&X[0],&X[m]);n=unique(&X[0],&X[m])-X;
        double s=0;
        for(int i=0;ii){
            int l=lower_bound(&X[0],&X[n],L[i].l)-X,r=lower_bound(&X[0],&X[n],L[i].r)-X-1;    
            update(1,0,n-1,l,r,L[i].v);
            s+=sum[1]*(L[i+1].h-L[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++cas,s);
        //POJ上提交G++的同学请改为%.2f 
    }
    return 0;
}
扫描线

你可能感兴趣的:([POJ1151][HDU1542]Atlantis(线段树,扫描线))