欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POJ 1151 Atlantis (扫描线+线段树)

程序员文章站 2022-07-13 11:10:31
...

Description:

平面内给出n个矩形,问这些矩形覆盖的总面积是多少。

POJ 1151 Atlantis (扫描线+线段树)

Input:

n,以及每个矩形的两个对角点坐标。

Output:

总的覆盖面积

Analysis:

首先将边离散化是必要的,毕竟平面上只有在矩形上的边才需要考虑,为什么不考虑点呢?因为边才是直接决定面积的因素,然后可以想到将矩形按照每条线分割,然后计算每个分割的矩形面积。如下图

POJ 1151 Atlantis (扫描线+线段树)

这样我们就有了一个O(n^3)的方案,枚举每个矩形,每两条相邻横边,每两条相邻枞边,若该横纵边围成的矩形在所枚举的矩形内部,则答案加上该矩形的面积。

但是,每次都枚举每一个矩形来判断是否加入面积是可以优化的,如果每次将竖线从左网右边扫,并且记录下每条竖线是一个矩形的左边还是右边的话就可以知道改条边是否会影响下一个区域的面积了。然而一条竖线上可能有很多个矩形的边重叠在一起,而各个矩形的横边将区域上的一条枞边分割成几个区间,也就是说在扫描时要能够维护该区间中有多长的有效线段是会影响下一个矩形的面积的,这里可以考虑用线段树,对区间进行修改。假设每条竖线上的的有效长度为len,横坐标为x1,下一条相邻竖线横坐标为x2,则每次的答案就累加len*(x2-x1), 时间复杂度O(n^2logn)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<sstream>
#include<cmath>
#include<iterator>
#include<bitset>
#include<stdio.h>
#include<time.h>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long LL;
const int INF = 0xffffff0;
const int MOD = 1e9 + 7;
const int maxn = 105;

struct Node{
    int L,R;
    double len;
    int covers;
    int Mid(){return L+(R-L)/2;}
};
struct Line{
    double x,y1,y2;
    bool isleft;
    bool operator <(const Line & rhs)const{
        return x<rhs.x;
    }
};
int n;
double y[maxn*2];
int yc;

Node tree[maxn*2*4];
Line line[maxn*2];
int lc;

void Build(int root,int L,int R){
    Node & now=tree[root];
    now.L=L,now.R=R;
    now.covers=0,now.len=0;
    if(L==R)return;
    int mid=now.Mid();
    Build(2*root+1,L,mid);
    Build(2*root+2,mid+1,R);
}

void Insert(int root,int L,int R){
    Node & now=tree[root];
    if(now.L==L&&now.R==R){
        now.len=y[R+1]-y[L];
        now.covers++;
        return;
    }
    int mid=now.Mid();
    if(R<=mid)Insert(2*root+1,L,R);
    else if(L>mid)Insert(2*root+2,L,R);
    else {
        Insert(2*root+1,L,mid);
        Insert(2*root+2,mid+1,R);
    }
    if(now.covers==0){
        now.len=tree[2*root+1].len+tree[2*root+2].len;
    }
}

void Delete(int root,int L,int R){
    Node &now=tree[root];
    if(now.L==L&&now.R==R){
        now.covers--;
        if(now.covers==0){
            if(L==R) now.len=0;
            else now.len=tree[root*2+1].len+tree[root*2+2].len;
        }
        return;
    }
    int mid=now.Mid();
    if(R<=mid)Delete(2*root+1,L,R);
    else if(L>mid)Delete(2*root+2,L,R);
    else {
        Delete(2*root+1,L,mid);
        Delete(2*root+2,mid+1,R);
    }
    if(now.covers==0){
        now.len=tree[2*root+1].len+tree[2*root+2].len;
    }
}

int main() {

	int kase=0;
    while(scanf("%d",&n)&&n){
        yc=0;lc=0;
        _for(i,0,n){
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[yc++]=y1;
            y[yc++]=y2;
            line[lc].x=x1,line[lc].y1=y1,line[lc].y2=y2;
            line[lc].isleft=true;
            lc++;
            line[lc].x=x2,line[lc].y1=y1,line[lc].y2=y2;
            line[lc].isleft=false;
            lc++;
        }

        sort(y,y+yc);sort(line,line+lc);
        yc=unique(y,y+yc)-y;

        Build(0,0,yc-1-1);

        double ans=0;
        for(int i=0;i<lc-1;++i){
            Line & now=line[i];
            int y1=lower_bound(y,y+yc,now.y1)-y;
            int y2=lower_bound(y,y+yc,now.y2)-y;
            if(y1>y2) swap(y1,y2);
            if(now.isleft)Insert(0,y1,y2-1);
            else Delete(0,y1,y2-1);
            ans+=tree[0].len*(line[i+1].x-now.x);
        }
        printf("Test case #%d\n",++kase);
        printf("Total explored area: %.2f\n\n",ans);
    }


	return 0;
}