https://blog.csdn.net/sugarbliss/article/details/80568257
https://blog.csdn.net/u013480600/article/details/22548393
上面的博客是我为了理解离散化找的两篇较好的博文,两个人在实现上不同。
第一位同志的讲解和代码比较短,我的代码思路是看第一位同志的。两个人在帮助理解离散化和扫描线方面都写得比较好,认真看不浮躁的话,都是很容易理解的——尽管我从无离散化基础,有线段树基础理解这个花了四天,中间还要上课……玩……
但如果要形象理解离散化,看第二位同志的图
但是第一位同志说 "l==r 这是一个点而不是线段" ——在他的pushup函数中
这在我的代码思想中不是这样的,尽管我仿照他的写,结果差不多。但在我看来这是一条最基本的元段,
只不过——见我的下面注释“需要注意的事项.3” + 坐标离散化的思路
/*
需要注意的事项
1.N是输入的区域个数,那么线段树数组长度,点数组长度,边数组长度都要好好考虑
2.只有自己的上边才能消除自己下边的的cnt标记
如果大段的cnt非零(还没遇到自己的上边) 小段更新不影响大段原来的值
否则要依靠小段更新
3.my_flush的第二个判断语句else if(l==r) sum[rt] = 0 ;是有必要的
如果他的cnt标记被消除了,那么他是无法通过下面的sum[rt] =sum[rt<<1] + sum[rt<<1|1];来更新自己的值的
只能自己置零,本来较小的sum[rt<<1]和sum[rt<<1|1]都是零 是可以通过这个来置零的
但是如果是较大的N配上较大的rt值(只有最小的不可分割的一段),
那么sum[rt<<1]和sum[rt<<1|1]会发生数组越界就会出现不可知的值
如果你将sum开成maxn的32倍就不会出现这种情况——已经试验过了 的确是这个原因 改了就能过
4.if(L<=l&&r<=R)不要写反成if(l<=L&&R<=r) 基本功要扎实
5.unique后的结果是点的个数,减掉一是线段的个数,再减一是从0开始计数的最后一个元段的实际下标
*/
/*坐标离散化的思路
一开始我以为是将每个浮点数点投影成整数点
而实际上这里做的是
将横坐标浮点数组进行升序排序
两个相邻的点构成元段——线段树的最小单位
如何确定一条边对应哪些元段呢?
我们通过lower_bound来确定这条边的起始元段和终止元段
*/
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,a,b) for(ll i=a;i<b;++i)
#define maxn 105
typedef struct ed {
double l, r, h;
int f;
} ed;
ed e[maxn << 1];
int cmp(ed a, ed b) {
return a.h < b.h;
}
double sum[maxn << 3], x[maxn << 1], ans; //N条边 2N个点 最多2*N-1个小段
int cnt[maxn << 3];
int N, run = 0;
void my_flush(int l, int r, int rt)
{
if (cnt[rt]) sum[rt] = x[r + 1] - x[l] ;
else
{
if (l == r) sum[rt] = 0 ;
else sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
}
void update(int L, int R, int gx, int l, int r, int rt)
{
if (l > R || r < L) return ;
if (L <= l && r <= R)
{
cnt[rt] += gx;
my_flush(l, r, rt);
return ;
}
int m = (r + l) >> 1 ;
update(L, R, gx, lson); update(L, R, gx, rson);
my_flush(l, r, rt);
}
int main()
{
while (~scanf("%d", &N) && N)
{
++run; ans = 0;
double a, b, c, d;
rep(i, 0, N)
{
scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
x[2 * i] = a;
e[2 * i].l = a, e[2 * i].r = c, e[2 * i].h = b; e[2 * i].f = 1;
x[2 * i + 1] = c;
e[2 * i + 1].l = a, e[2 * i + 1].r = c, e[2 * i + 1].h = d; e[2 * i + 1].f = -1;
}
sort(x, x + 2 * N);
sort(e, e + 2 * N, cmp);
/*坐标去重*/
int ls = unique(x, x + 2 * N) - x - 1 ; //ls 代表有多少个小段 从0开始计数
memset(sum, 0, sizeof(sum)); memset(cnt, 0, sizeof(cnt));
double last_h = 0 , now_h = 0;
rep(i, 0, 2 * N)
{
now_h = e[i].h;
ans += (sum[1] * (now_h - last_h)); last_h = now_h;
int l = lower_bound(x, x + ls, e[i].l) - x;
int r = lower_bound(x, x + ls, e[i].r) - x - 1;
update(l, r, e[i].f, 0, ls - 1, 1);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", run, ans);
}
return 0;
}