AcWing 247. 亚特兰蒂斯 | 扫描线
题目描述
有几个古希腊书籍中包含了对传说中的亚特兰蒂斯岛的描述。
其中一些甚至包括岛屿部分地图。
但不幸的是,这些地图描述了亚特兰蒂斯的不同区域。
您的朋友Bill必须知道地图的总面积。
你自告奋勇写了一个计算这个总面积的程序。
输入格式
输入包含多组测试用例。
对于每组测试用例,第一行包含整数n,表示总的地图数量。
接下来n行,描绘了每张地图,每行包含四个数字x1,y1,x2,y2x1,y1,x2,y2(不一定是整数),(x1,y1)(x1,y1)和(x2,y2)(x2,y2)分别是地图的左上角位置和右下角位置。
当输入用例n=0时,表示输入终止,该用例无需处理。
输出格式
每组测试用例输出两行。
第一行输出”Test case #k”,其中k是测试用例的编号,从1开始。
第二行输出“Total explored area:a”,其中a是总地图面积(即此测试用例中所有矩形的面积并,注意如果一片区域被多个地图包含,则在计算总面积时只计算一次),精确到小数点后两位数。
在每个测试用例后输出一个空行。
数据范围
1≤n≤100,
0≤x1<x2≤100000,
0≤y1<y2≤100000
输入样例:
2
10 10 20 20
15 15 25 25.5
0
输出样例:
Test case #1
Total explored area: 180.00
题意:求面积并
题解:扫描线。
如果题目给出的矩形如下:
那么我们要求的总面积为:
我们如果用一条竖直的直线从左到右扫过整个坐标系,那么直线上在所求面积中的覆盖长度L只会在每个矩形的左右边界发生变化,整个图形可以被分成2*n段,每一段直线被覆盖的长度L都是固定的,这一段的面积为L*∆x。
我们知道∆x是两个相邻的x之间的间距,那我们要怎么维护每一段L的长度?
对于每个矩形,我们把左边界记为一个四元组:(x1,y1,y2,1),把右边界记为(x2,y1,y2,-1)(假设x1<x2,y1<y2)。我们可以按纵坐标维护一个线段树,当直线扫过矩形的左边界时,相当于在线段树中把区间[y1,y2](y1<y2)的值都加上1(插入区间[y1,y2]),当直线扫过矩形的右边界时,相当于在线段树中把区间[y1,y2]的值都减去1(减去区间[y1,y2]),只要有位置值大于0就代表这是线段的一部分(算区间长度)。因为y的范围很大所以我们要先离散化一下。我们在线段树中定义cnt来记录这段区间出现了多少次,定义len来记录直线长度。每次将直线更新之后,根结点中存的长度就是当前段L的大小。
代码:
#include <bits/stdc++.h> using namespace std; #define ll long long const int N = 200 + 10; struct node{ double x,y1,y2; int val; node() {} node(double x, double y1, double y2,int val){ this->x = x; this->val = val; this->y1 = y1; this->y2 = y2; } bool operator <(const node &t)const { return x<t.x; } }; struct Tree{ int l,r,cnt; double len; }t[1000010]; //找了一个小时的段错误,结果是*4小了??? int n,k=1; vector<node> a; vector<double> y; void up(int rt) { if (t[rt].cnt>0) t[rt].len = y[t[rt].r+1] - y[t[rt].l]; else t[rt].len = t[rt<<1].len + t[rt<<1|1].len; } void build(int rt,int l,int r) { t[rt].l = l,t[rt].r = r; t[rt].cnt = 0; t[rt].len = 0; if (l == r) return; int mid = (l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } void update(int rt,int l,int r,int val) { if (l <= t[rt].l && r >= t[rt].r) { t[rt].cnt+=val; up(rt); return; } int mid = (t[rt].l+t[rt].r)>>1; if (l <= mid) update(rt<<1,l,r,val); if (r > mid) update(rt<<1|1,l,r,val); up(rt); } int main() { while (~scanf("%d",&n) && n) { y.clear();a.clear(); for(int i = 0; i < n; i++) { double x1,x2,y1,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); y.push_back(y1); y.push_back(y2); a.push_back(node(x1,y2,y1,1)); a.push_back(node(x2,y2,y1,-1)); } y.push_back(-1); sort(a.begin(),a.end()); sort(y.begin(),y.end()); y.erase(unique(y.begin(),y.end()),y.end()); int m = y.size(),num = a.size(); build(1,0,m); double ans = 0; for (int i = 0; i < num; i++) { int l = lower_bound(y.begin(),y.end(),a[i].y2)-y.begin(); int r = lower_bound(y.begin(),y.end(),a[i].y1) - y.begin()-1; update(1,l,r,a[i].val); ans += t[1].len*(a[i+1].x-a[i].x); } printf("Test case #%d\n",k++); printf("Total explored area: %.2f\n\n",ans); } return 0; }
PS:注意数组开大一点