线段树 求矩形并 面积

http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html

给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1)、(x2,y2),对这样的一个矩形,我们构造两条线段,一条定位在x1,它在y坐标的区间是[y1,y2],并且给定一个cover域值为1;另一条线段定位在x2,区间一样是[y1,y2],给定它一个cover值为-1。根据这样的方法对每个矩形都构造两个线段,最后将所有的线段根据所定位的x从左到右进行排序。

上图中,红色的字体表示的是该线段的cover值。刚刚开始的时候,线段树上的cover值都为0,但第一根线段(x==0)插入线段树的之后,我们将线段树上的cover加上该线段的cover,那么,此时线段树上被该线段覆盖的位置上的cover的值就为1,下次再插入第二根线段(x==1)此时发现该线段所覆盖的区间内,有一部分线段树的cover为0,另有一部分为1,仔细观察,但插入第二个线段的时候,如果线段树上cover已经为1的那些区间,和现在要插入的第二根线段之间,是不是构成了并面积?还不明白?看下图,绿色部分即为插入第二根线段后得到的并面积



在插入第三条线段时,计算的是1,2,3,4块的面积,在插入第四条线段时,计算5的面积

够清楚了吧!也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的cover是否大于等于1,如果是,那么就可以将并面积值加上(目前线段的x定位 - 上一线段的x定位)*(该区间的大小)

View Code
复制代码
  1 #include <iostream>
2 #include <algorithm>
3 using namespace std;
4 const int maxn=110;
5
6 struct LINE
7 {
8 double x, y_down, y_up;
9 int flag;
10 bool operator<(const LINE &a)const
11 {
12 return x<a.x;
13 }
14 }line[2*maxn];
15
16 struct TREE
17 {
18 double y_down, y_up;
19 double x;
20 int cover; //用以表示加进线段树中的线段次数
21 bool flag; //此标记用来表示是否有超元线段;为了处理方便加上去的
22 }tree[1000*maxn];
23
24 int n;
25 double x1, y1, x2, y2;
26 int index=0;
27 double y[2*maxn];
28
29 void build(int i, int l, int r)
30 {
31 tree[i].x = -1; //-1表示该区间已经没有线段
32 tree[i].cover = 0; //表示该区间上有多少条线段;左边线段加进去则++,右边线段加进去则--
33 tree[i].y_down = y[l];
34 tree[i].y_up = y[r];
35 tree[i].flag = false;
36 if(l+1==r)
37 {
38 tree[i].flag = true; //flag==true表示达到了叶子节点
39 return;
40 }
41 int mid=(l+r)>>1;
42 build(2*i, l, mid);
43 build(2*i+1, mid, r);
44 }
45
46 double insert(int i, double x, double l, double r, int flag) //flag表示为左边还是右边
47 {
48 if (r<=tree[i].y_down || l>=tree[i].y_up)
49 return 0;
50 if (tree[i].flag)
51 {
52 if (tree[i].cover > 0) //递归到了叶子节点
53 {
54 double temp_x = tree[i].x;
55 double ans=(x-temp_x)*(tree[i].y_up - tree[i].y_down);
56 tree[i].x = x; //定位上一次的x
57 tree[i].cover += flag;
58 return ans;
59 }
60 else
61 {
62 tree[i].cover += flag;
63 tree[i].x = x;
64 return 0;
65 }
66 }
67 double ans1, ans2;
68 ans1 = insert(2*i, x, l, r, flag);
69 ans2 = insert(2*i+1, x, l, r, flag);
70 return ans1+ans2;
71 }
72
73 int main( )
74 {
75 // freopen("d:\\in.txt","r",stdin);
76 int count=0;
77 while (scanf("%d", &n)!=EOF&&n)
78 {
79 index = 1;
80 for (int i=1; i<=n; i++)
81 {
82 scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
83 y[index] = y1;
84 line[index].x = x1;
85 line[index].y_down = y1;
86 line[index].y_up = y2;
87 line[index].flag = 1; //1表示左边
88
89 index++;
90 y[index] = y2;
91 line[index].x = x2;
92 line[index].y_down = y1;
93 line[index].y_up = y2;
94 line[index].flag = -1; //-1表示右边
95 index++;
96 }
97 sort(&y[1], &y[index]); //把所有的纵坐标按从小到大排序,把1写成了0,WA一次
98 sort(&line[1], &line[index]);
99 build(1, 1, index-1);
100 double ans=0;
101 for (i=1;i<index; i++)
102 {
103 ans += insert(1, line[i].x, line[i].y_down, line[i].y_up, line[i].flag);
104 }
105 printf("Test case #%d\nTotal explored area: %.2f\n\n", ++count, ans);
106 }
107 return 0;
108 }
复制代码


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值