扫描线求矩形面积并,使用线段树维护(感觉非常详细)

HDOJ1542 Atlantis

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542

题意:求平面中矩形面积并。

 

这张图阴影和空白交替标出了并的面积。

我是习惯从下向上扫描的。

根据之前的一道CF EducationalRound的题,可以知道Ox坐标下扫描线的基本思路:

“入”和“出”分别用-1和1来代替(但是这里需要在线段树中记录区间内边的条数,所以我把-1和1调换了位置,并且在cmp函数里修改了这个比较)

但是平面的话,想记录每一列的宽度情况,所以可以用线段树来维护:线段树中的区间[l,r]代表[l,r]这个大区间有多长的子区间是被覆盖的。当然首先要离散化,不过存储这个覆盖的长度仍然是未离散化的长度。

那么就会有类似于线段树中“区间合并”的问题了,但是这题很特殊,不需要区间合并

特殊在哪里?

一般的区间合并希望查询特定的[l,r]区间内被覆盖的子区间的总长度,但是这里不需要查询特定的[l,r]区间,而是整棵线段树描述的区间的覆盖总长度

所以我们很轻易地想到,在update到一个需要覆盖的子区间的时候,向上传递这个子区间的长度。

还会有一个问题:假如我在线段树上更新了一个表示区间[l,r]的节点rt,这个区间的父亲RT(假如表示区间[L, R],其中L<=l, r<=R)有没有可能被覆盖呢?

这里就需要分情况讨论一下:

1:RT表示的区间已经被完全覆盖过了

2:RT表示的区间并没有被覆盖过,或者被覆盖了一部分

对于情况1

很好理解。在这个大区间下,由加了一条包含在[L,R]里的线段,类似这样:

那我还需要使用[l,r]更新[L,R]的答案吗?很明显不需要。只需要用[L,R]代表的未离散化的长度更新一下当前值就行了。

问题:为什么不用[l,r]更新[L,R],但是还要用未离散化的长度更新一下当前值?

因为pushup操作不只是发生在发现小区间,还要在某区间的左右儿子均更新结束后,向上传第一次。

对于情况2

假如RT还没有覆盖过,或者覆盖了一部分:

显然RT的覆盖部分是从左右儿子代表的子区间更新来的,那么直接用左右儿子的覆盖区间长度和更新就行。

但是,假如RT代表的覆盖区间是一个点,即叶子的时候。显然一个点的区间长度是0,这点要注意特判一下。

这样就没有问题了,顺便,感觉自己的离散化姿势很好。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define lrt rt << 1
 5 #define rrt rt << 1 | 1
 6 const int maxn = 210;
 7 typedef struct Seg {
 8     double len;
 9     int cnt;
10 }Seg;
11 typedef struct Event {
12     double l, r, h;
13     int sign;
14 }Event;
15 
16 int n, hcnt;
17 double h[maxn];
18 vector<Event> event;
19 Seg seg[maxn<<2];
20 
21 bool cmp(Event a, Event b) {
22     if(a.h != b.h) return a.h < b.h;
23     return a.sign > b.sign;
24 }
25 
26 int id(double v) {
27     return lower_bound(h, h+hcnt, v) - h + 1;
28 }
29 
30 void build(int l, int r, int rt) {
31     if(l == r) return;
32     seg[rt].len = .0, seg[rt].cnt = 0;
33     int mid = (l + r) >> 1;
34     build(l, mid, lrt);
35     build(mid+1, r, rrt);
36 }
37 
38 void pushup(int l, int r, int rt) {
39     if(seg[rt].cnt) seg[rt].len = h[r] - h[l-1];
40     else {
41         if(l == r) seg[rt].len = 0;
42         else seg[rt].len = seg[lrt].len + seg[rrt].len;
43     }
44 }
45 
46 void update(int L, int R, int sign, int l, int r, int rt) {
47     if(L <= l && r <= R) {
48         seg[rt].cnt += sign;
49         pushup(l, r, rt);
50         return;
51     }
52     int mid = (l + r) >> 1;
53     if(L <= mid) update(L, R, sign, l, mid, lrt);
54     if(mid < R) update(L, R, sign, mid+1, r, rrt);
55     pushup(l, r, rt);
56 }
57 
58 int main() {
59     // freopen("in", "r", stdin);
60     int _ = 1;
61     double ax, ay, bx, by;
62     while(~scanf("%d", &n) && n) {
63         event.clear(); hcnt = 0;
64         for(int i = 0; i < n; i++) {
65             scanf("%lf%lf%lf%lf",&ax,&ay,&bx,&by);
66             event.push_back(Event{ax, bx, ay, 1});
67             event.push_back(Event{ax, bx, by, -1});
68             h[hcnt++] = ax; h[hcnt++] = bx;
69         }
70         sort(event.begin(), event.end(), cmp);
71         sort(h, h+hcnt); hcnt = unique(h, h+hcnt) - h;
72         build(1, hcnt, 1);
73         double ret = .0;
74         for(int i = 0; i < event.size(); i++) {
75             int l = id(event[i].l);
76             int r = id(event[i].r) - 1;
77             int sign = event[i].sign;
78             update(l, r, sign, 1, hcnt, 1);
79             cout << event[i+1].h - event[i].h << endl;
80             ret += (event[i+1].h - event[i].h) * seg[1].len;
81         }
82         printf("Test case #%d\n", _++);
83         printf("Total explored area: %.2f\n\n", ret);
84     }
85     return 0;
86 }

 

转载于:https://www.cnblogs.com/kirai/p/6807801.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值