Atlantis
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 16397 Accepted Submission(s): 6691
The input file is terminated by a line containing a single 0. Don’t process it.
Output a blank line after each test case.
题意:在一个二位平面上放置矩形,给出矩形的左下角坐标和右上角坐标,求整个二位平面的覆盖面积
思路:扫描线
先看一张我手画的样例草图
看上面的图,我做的扫面线是横线(与X轴平行),从下往上扫描
存进一个数组里从小到大排序,用下标做离散化
所以依次扫描的线是 1 线 -> 2线 -> 3线 -> 4线
每条线存了 两个x值(线段的左右端点),一个y值(线段高度),还有一个flag标记(1为矩形的下线,-1为上线)
扫描第一条线的时候,在下标数组中找到这条线的左右端点值所在的下标,然后update进线段树中
第一条线插入完毕后 tree[1].sum = 10 (sum表示整个区间的有效线段长度)
所以面积 += 2线与1线的高度差 * tree[1].sum = 5 * 10 = 50
此时算出了 1 面 的面积
扫描第二条线的时候,它是一条下线,会把区间长度更新为 15,注意,获取长度的操作是利用线段树中线段端点差值来获取的
而不是通过插入的线段的值,所以更新之后获取的长度是 15,而不是叠加的20。
面积 += 2线与3线的高度差 * tree[1].sum = 75
扫描第三条线的时候发现是 上线,更新完区间之后,有效长度为 10
计算出最后一块面积55
所以总面积是 180
最需要注意的就是区间问题,一般的线段树区间是分成 [1,2][3,4][4,5]这样子的,但是这样会丢失区间
比如我们需要 [2,3]的时候就找不到这个区间了,所以扫面线的线段树需要改造
我们把 [0,0] 表示为线段 [0,1] [1,1]表示线段 [1,2] [0,3]表示线段 [0,4]
所以在插入线段的时候 要插入 ( l , r -1 ) , 计算区间的时候 用 pos[r + 1] - pos[l] 就可以了
贴上代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 100005
#define mem(a,x) memset(a,x,sizeof(a))
#define debug printf("********");
struct line{
double l,r,h;
int flag;
line(){}
line(double ll,double rr,double hh,int fflag)
: l(ll),r(rr),h(hh),flag(fflag){}
}lines[maxn];
struct node{
int l,r,date;
double sum;
}tree[5000];
int lazy[maxn * 4];
double pos[maxn];
bool cmp(line a,line b){
return a.h < b.h;
}
void push_up(int root){
if(lazy[root] > 0){
tree[root].sum = pos[tree[root].r + 1] - pos[tree[root].l];
}
else if(tree[root].l == tree[root].r){
tree[root].sum = 0;
}else{
tree[root].sum = tree[root << 1].sum + tree[root << 1 | 1].sum;
}
}
void build_tree(int root,int l,int r){
tree[root].l = l;
tree[root].r = r;
tree[root].sum = 0;
if(l == r)
return ;
int mid = (l + r) >> 1;
build_tree(root << 1,l,mid);
build_tree(root << 1 | 1,mid + 1,r);
}
void update(int root,int l,int r,int val){
if(tree[root].l >= l && tree[root].r <= r){
lazy[root] += val;
push_up(root);
return ;
}
int mid = (tree[root].l + tree[root].r) >> 1;
if(l <= mid)
update(root << 1,l,r,val);
if(r > mid)
update(root << 1 | 1,l,r,val);
push_up(root);
}
int discre(int n){
int tmp = 1;
for(int i = 1;i < n;i++){
if(pos[i] != pos[i - 1]){
pos[tmp++] = pos[i];
}
}
return tmp;
}
int main(){
int n,cnt,num,cas = 1;
double x1,y1,x2,y2;
while(scanf("%d",&n) != EOF){
if(n == 0)
break;
cnt = num = 0;
mem(lazy,0);
for(int i = 1;i <= n;i++){
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
lines[cnt++] = line(x1,x2,y1,1);
lines[cnt++] = line(x1,x2,y2,-1);
pos[num++] = x1;
pos[num++] = x2;
}
sort(lines,lines + cnt,cmp);
sort(pos,pos + num);
num = discre(num);
double area = 0.0;
build_tree(1,0,num - 1);
for(int i = 0;i < cnt - 1;i++){
int l = lower_bound(pos,pos + num,lines[i].l) - pos;
int r = lower_bound(pos,pos + num,lines[i].r) - pos - 1;
update(1,l,r,lines[i].flag);
area += (lines[i + 1].h - lines[i].h) * tree[1].sum;
}
printf("Test case #%d\n",cas++);
printf("Total explored area: %.2f\n\n",area);
}
}