zoukankan      html  css  js  c++  java
  • [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<cstdio>
    #include<cstring>
    #include<algorithm>
    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;i<n;++i){
                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;i<m;++i){
                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
    Total explored area: %.2lf
    
    ",++cas,s);
            //POJ上提交G++的同学请改为%.2f 
        }
        return 0;
    }
    扫描线
  • 相关阅读:
    [工作中的设计模式]中介模式模式Mediator
    [工作中的设计模式]责任链模式chain
    [工作中的设计模式]迭代子模式Iterator
    [工作中的设计模式]组合模式compnent
    TI IPNC Web网页之流程分析
    TI IPNC Web网页之GoDB开发环境
    安装ubuntu时将boot目录单独挂载的意义
    ubuntu添加自定义vga输出分辨率
    GCC编译默认的头文件搜索路径
    设置搜狗输入法在任何时候按左右两侧的shift激活
  • 原文地址:https://www.cnblogs.com/gosick/p/11265295.html
Copyright © 2011-2022 走看看