zoj 1659 Mobile Phone Coverage(矩形面积并)

题意:每组数据给出正方形中点坐标及半边长,求矩形面积并;

思路:采用沿垂直方向计算矩形面积并的方法,把面积切成若干垂直条再累加。zoj上能过,但Uva688却一直RE,已经尝试过开大空间了。。。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const double epsi=1e-10;
const int maxn=40000;
struct line{
    double x,y1,y2;   //x坐标及上下端点y坐标
    int s;
    line(double a=0,double b=0,double c=0,int d=0):x(a),y1(b),y2(c),s(d){}
    bool operator <(const line &op2) const{
        return x<op2.x; //按x坐标递增排序
    }
};
extern double ly[maxn<<1];   //上下边的y坐标
class Tree{
    int cover;             //并区间标志
    Tree *child[2];        //左右儿子指针
    void deliver(){        //调整覆盖
        if(cover)   //若并区间未结束,则覆盖区间长度为ly[r]-ly[l];否则覆盖区间长度为左右子树的覆盖区间长度之和
            len=ly[r]-ly[l];
        else
            len=child[0]->len+child[1]->len;
    }
public:
    int l,r;
    double len;       //垂直条长度
    void setup(int ll,int rr){
        l=ll;r=rr;
        cover=0;len=0;
        if(ll+1==rr) return;  //若区间无法二分,则返回
        int mid=(l+r)/2;
        child[0]=new Tree(),child[1]=new Tree();
        child[0]->setup(ll,mid),child[1]->setup(mid,rr);//构造左右子树
    }
    void paint(const int &ll,const int &rr,const int &v){ //往区间为[l,r]的线段树插入边界标志为v的垂直条[ll,rr]
        if(ll>=r||rr<=l) return;
        if(ll<=l&&r<=rr){     //若[ll,rr]覆盖[l,r]则调整区间长度len
            if(cover+=v) len=ly[r]-ly[l];
            else{
                if(child[0]==NULL) len=0;
                else
                len=child[0]->len+child[1]->len;
            }
            return;
        }
        child[0]->paint(ll,rr,v),child[1]->paint(ll,rr,v);//递归左右子树
        deliver();//调整覆盖区间长度
    }
    void del(){
        if(child[0]){ //若左子树存在,则递归删除左右子树
            child[0]->del();
            delete child[0];
            child[1]->del();
            delete child[1];
        }
    }
};
int cas=0;
int n,m,tot,ty;  //l表的长度为tot,ly表的长度为ty
line l[maxn<<1];  //存储垂直条
double ly[maxn<<1]; //存储地图上下边的y坐标
Tree *seg;   //线段树指针
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        tot=ty=0;
        for(int i=0;i<n;i++){
            double x,y,r;
            scanf("%lf%lf%lf",&x,&y,&r);
            l[tot++]=line(x-r,y-r,y+r,1);
            l[tot++]=line(x+r,y-r,y+r,-1);
            ly[ty++]=y-r;ly[ty++]=y+r;
        }
        sort(l,l+tot);
        sort(ly,ly+ty);
        ty=unique(ly,ly+ty)-ly;//使用unique函数,除去ly[]中的重复元素
        double ans=0;
        seg=new Tree();
        seg->setup(0,ty-1);
        for(int i=0,j;i<tot;i=j){
            if(i) ans+=seg->len*(l[i].x-l[i-1].x);
            j=i; //依次枚举右方的垂直条,取出底边的y坐标在ly中的序号l,顶边的y坐标在ly中的序号r,左右边界标志k,将[l,r,k]插入线段树
            while(j<tot&&fabs(l[i].x-l[j].x)<=epsi){
                  seg->paint(lower_bound(ly,ly+ty,l[j].y1)-ly,lower_bound(ly,ly+ty,l[j].y2)-ly,l[j].s);
                  ++j;
            }
        }
        seg->del();
        delete seg;
        printf("%d %.2f\n",++cas,ans);
    }
    return 0;
}

 

posted on 2015-06-04 09:20  大树置林  阅读(214)  评论(0编辑  收藏  举报

导航