hdu1524线段树,扫描线

来源:互联网 发布:linux 时间格式 编辑:程序博客网 时间:2024/06/03 06:43

http://acm.hdu.edu.cn/showproblem.php?pid=1542

题意:
一些相互可能重叠的矩形,让你求他们实际覆盖的面积之和;
对于每个矩形,给你左上角坐标,以及右下角顶点坐标

#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>using namespace std;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1const int maxn=50009;//cnt记录此区间上的上面的边和下面的边条数之差int n,cnt[maxn<<2],tmp,res;//sum记录区间覆盖的长度double sum[maxn<<2];//x[]记录离散化以后的坐标double x[maxn];struct node{    //线段的左右两个端点的横坐标以及纵坐标    double a,b,h;    //标记是矩形上面的边还是下面的边    int d;    node(double x=0,double y=0,double z=0,int aa=0)    {        a=x,b=y,h=z,d=aa;    }}line[maxn];int cmp(node a,node b){    return a.h<b.h;}void pushup(int rt){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];    //cnt为-1代表此区间的边数覆盖并不是均匀的,也就是左右两个区间    //上下边的覆盖条数不一样    if(cnt[rt<<1]==-1||cnt[rt<<1|1]==-1)        cnt[rt]=-1;    else if(cnt[rt<<1]!=cnt[rt<<1|1])        cnt[rt]=-1;    else cnt[rt]=cnt[rt<<1];}void pushdown(int rt,int l,int r){    int m=l+r>>1;    if(cnt[rt]!=-1)    {        cnt[rt<<1]=cnt[rt<<1|1]=cnt[rt];        //如果边数覆盖均匀,则可以计算区间的覆盖长度        //注意区间长度计算时的端点选取        sum[rt<<1]=cnt[rt<<1]?x[m+1]-x[l]:0;        sum[rt<<1|1]=cnt[rt<<1|1]?x[r+1]-x[m+1]:0;    }}void build(int l,int r,int rt){    if(l==r)    {        sum[rt]=0;        cnt[rt]=0;        return;    }    int m=l+r>>1;    build(lson);    build(rson);    pushup(rt);}void update(int L,int R,int val,int l,int r,int rt){    if(L<=l&&r<=R)    {        if(cnt[rt]!=-1)        {              //只有覆盖均匀的区间才可以直接加减更新              cnt[rt]+=val;              sum[rt]=cnt[rt]?x[r+1]-x[l]:0;              //注意return 在if里面              //如果区间不均匀则应该直到区间均匀分布后在更新              return;        }    }    pushdown(rt,l,r);    int m=l+r>>1;    if(m>=L) update(L,R,val,lson);    if(m<R) update(L,R,val,rson);    pushup(rt);}int fin(double xx){    int l=1,r=tmp;    while(l<=r)    {        int m=l+r>>1;        if(x[m]==xx) return m;        else if(xx>x[m]) l=m+1;        else r=m-1;    }    return -1;}int main(){    int ase=1;    while(scanf("%d",&n)&&n)    {        double a,b,c,d;        res=1;        for(int i=0;i<n;i++)        {          scanf("%lf%lf%lf%lf",&a,&b,&c,&d);          x[res]=a;          line[res++]=node(a,c,d,-1);          x[res]=c;          line[res++]=node(a,c,b,1);        }        sort(x+1,x+1+res);        sort(line+1,line+1+res,cmp);        tmp=1;        //去重        for(int i=2;i<=res;i++)            if(x[i]!=x[i-1])            x[++tmp]=x[i];        //注意这里,我们实际线段树维护的是区间标号,若有n个端点,则实际只有n-1个区间        build(1,tmp-1,1);        double ss=0.0;        for(int i=1;i<res;i++)        {            int l=fin(line[i].a);            //注意这里要减一,得到的才是正确的区间标号            int r=fin(line[i].b)-1;            if(l<=r)            update(l,r,line[i].d,1,tmp-1,1);            ss+=sum[1]*(line[i+1].h-line[i].h);        }        printf("Test case #%d\n",ase++);        printf("Total explored area: %.2f\n\n",ss);    }    return 0;}
原创粉丝点击