Description
Input
The input file is terminated by a line containing a single 0. Don't process it.
Output
Output a blank line after each test case.
Sample Input
2 10 10 20 20 15 15 25 25.5 0
Sample Output
Test case #1 Total explored area: 180.00
小红哥吐槽版区开始啦 :(PS: 欢迎各位高手,相互吐槽,商讨武林秘籍,增加内在修为 )
—— >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>..
吐槽L:加上今天,扫描线已经搞了两天了,本打算一上午就搞会了,没想到搞毁了。哎,我扫, 我扫, 我再扫。 扫来扫去,终于快“扫”完了。。。。。
先说下,扫描线,这是个好东西啊,你说明明就是个求面积,用个尺子一量,咔擦咔擦,整吧整吧不就完了吗,但是好像没那么简单,数学中的思想,矩形只要求得长和宽,那么面积就迎刃而解了。说是很简单,但是如何用计算机编程来实现它呢?这确实需要思考一下,计算机是按指令来执行任务的,我不能想让它乘哪个边就乘哪个边,想让它加哪个编就加哪个边。所以这确实需要好好思考一下:那么问题就来了,我如何把这些边的信息存起来呢,对,可以用结构体,具体要存那些信息呢,以抓图为例:
看图中的扫描线,此道题我是从左往右扫,需要存的信息是黑体实线的信息,把每条黑体实线的横坐标和上下顶点的纵坐标保存。
举个例子,存(10, 15)和 (10, 25)这条边的信息,
那么我存的东西为 line[1].x = 10,
line[1].y1 = 15,
line[1].y2 = 25;
line[1].f = 1(注意这里需要一个标记变量,来辨别此时这条边是否已经加入还是删除)。
其它黑体实线也这样存。
- 这样把每条黑体实线的横坐标和上下顶点的纵坐标存完后,就可以建立线段树(相信此时你已经了解线段树了),但是这里建的线段树和平时说的还是有区别的,比如要建一个区间1-4的线段树。如图所示:
-
-
但是这里要建的树和上面的还是要差别的。应该为:
这是为什么呢,为什么要按下面的方法建树,为什么是(l, mid, rt <<1)
(mid, r ,rt<<1 | 1)
而不是(l, mid, rt<<1)
( mid+1, r, rt<< 1 | 1)
这是因为如果按普通方法建树,那么(2, 3)区间就没法表示了,我更新信息的时候,(2, 3)区间也得用到,所以要按第二种方法建树。
建树这里也有讲究:这里要用到离散化,话说我上网一搜,搜到的东西没看不懂啊,这里用我自己的话说一下吧:你比如说我要以(10, 25.5)这个区间建树,我不会直接让根树的左区间==10, 右区间==25.5; 而是让左区间==1, 同时用个变量记录下10这个值, 让右区间==4,同时也用个变量记录一下25.5这个值。
如图所示:
此时可以定义结构体变量:
来存这些信息 则圈2的信息应该这样存:T[1].l = 1; T[1].r
T[1].lf =10; T[1].r = 25.5, 这样就可以了。 而c变量的作用是,判断当前边是否加入或者删除。
而建树代码这里就不贴了,为下文中的build函数。
说到这里,相信你已经把黑体实线的信息都存好了,而且根据这些信息,建立了一个特殊的线段树。
然后你需要排一下序,对 y 数组和 line 结构体数组,按从小到大存。
那么接下来应该怎么做呢?
那么接下来,我就要开始扫描了,这里也是最难理解的。说是扫描其实也可以说是边的加入和删除,扫面线扫到这条边,且这条边的标记变量为1,则加边,都则去掉此边(好像说的有点难理解了,额额)。
最难理解的部分,这里我想用代码讲解(代码有点挫。):
void calen(int rt)
{
if(T[rt].c > 0)//如果这段被覆盖,就更新这段的长度为实际长度
{
T[rt].cnt = T[rt].rf-T[rt].lf;
return ;
}
if(T[rt].l + 1 == T[rt].r) T[rt].cnt = 0; //如果这段被撤销,而且是最后的就把长度变为0
else T[rt].cnt = T[rt<<1].cnt + T[rt<<1|1].cnt;//如果被撤销但不是最后的,就加一下左右
}
void updata(int rt, Line e)//加入或者减去一条线段后的更新
{
if(e.y1==T[rt].lf && e.y2==T[rt].rf)
{
T[rt].c += e.f//改变覆盖情况
calen(rt);//返回此区间所表示的时间长度
return ;
}
if(e.y2<=T[rt<<1].rf) updata(rt<<1, e);//当你扫到的边的右端点不大于T[rt<<1].rf时,执行这条语句
else if(e.y1>=T[rt<<1|1].lf) updata(rt<<1|1, e);//当你扫到的边的左端点不小于T[rt<<1|1].lf时,执行这条语句
else//跨区间时,执行这条语句
{
Line temp;//把它分成左区间和右区间分别处理
temp = e;
temp.y2 = T[rt<<1].rf;
updata(rt<<1, temp);
temp = e;
temp.y1 = T[rt<<1|1].lf;
updata(rt<<1|1, temp);
}
calen(rt);//把根节点左孩子和右孩子的实际长度加起来存到此根结点上
}
最好能在纸上自己模拟一遍,比如把此题的第一条边加上,程序是怎么运行的。
其实求得面积是一块一块加起来的(图明天补上,晚上宿舍熄灯了,没法拍了,,,,sad,,,,),而且应该深刻理解每条边标记变量为1, 2, 0,或则先增在减小的含义,如果能理解这两个地方,这道题应该差不多了。。。。
好题难题不应该只做一遍,有空再做一遍,再来修改此篇题解。
好,现贴代码如下:
#include<stdio.h>
#include<algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#define MAXN 400
double y[MAXN];
int n;
using namespace std;
struct Line{
int f;
double x, y1, y2;
}line[MAXN];
struct node{
int l, r, c;
double lf, rf, cnt;
}T[MAXN<<2];
bool cmp(Line a, Line b)
{
return a.x < b.x;
}
void build(int l, int r, int rt)
{
T[rt].l = l;
T[rt].r = r;
T[rt].cnt = T[rt].c = 0;
T[rt].lf = y[l];
T[rt].rf = y[r];
if(l+1==r) return ;
int mid=(l+r)>>1;
build(l, mid, rt<<1);
build(mid, r, rt<<1|1);
}
void calen(int rt)
{
if(T[rt].c > 0)
{
T[rt].cnt = T[rt].rf-T[rt].lf;
return ;
}
if(T[rt].l + 1 == T[rt].r) T[rt].cnt = 0;
else T[rt].cnt = T[rt<<1].cnt + T[rt<<1|1].cnt;
}
void updata(int rt, Line e)
{
if(e.y1==T[rt].lf && e.y2==T[rt].rf)
{
T[rt].c += e.f;
calen(rt);
return ;
}
if(e.y2<=T[rt<<1].rf) updata(rt<<1, e);
else if(e.y1>=T[rt<<1|1].lf) updata(rt<<1|1, e);
else
{
Line temp;
temp = e;
temp.y2 = T[rt<<1].rf;
updata(rt<<1, temp);
temp = e;
temp.y1 = T[rt<<1|1].lf;
updata(rt<<1|1, temp);
}
calen(rt);
}
int main()
{
int i, cnt, k=1;
double x1, y1, x2, y2, ans;
while(~scanf("%d",&n)&&n)
{
cnt = 1;
ans = 0;
for(i=0; i<n; i++)
{
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
line[cnt].x = x1;
line[cnt].y1 = y1;
line[cnt].y2 = y2;
line[cnt].f = 1;
y[cnt++] = y1;
line[cnt].x = x2;
line[cnt].y1 = y1;
line[cnt].y2 = y2;
line[cnt].f = -1;
y[cnt++] = y2;
}
cnt--;
sort(y+1, y+cnt+1);
sort(line+1, line+cnt+1, cmp);
build(1, cnt, 1);
updata(1, line[1]);
for(i=2; i<=cnt; i++)
{
ans += T[1].cnt*(line[i].x-line[i-1].x);
updata(1, line[i]);
}
printf("Test case #%d\n", k++);
printf("Total explored area: %.2lf\n\n", ans);
}
return 0;
}