线段树扫描线(2---算矩形的相交面积)

HDU-1255

首先感谢一下 Titanium:http://acm.hdu.edu.cn/showproblem.php?pid=1255 从他的博客中得到了思路

怎么计算出重复的面积。

遇到这个求相交矩形的面积的时候,我第一反应就是将cnt标记下推,然后每次都将标记下推, 最后根据cnt的值来模仿1中求面积的方式来求,然后实现起来很复杂,并且估计会超时,所以就百度寻求了一波帮助。

我们先规定sum2 为 至少出现1次时统计的长度 sum为至少出现2次时的长度

如果某个区间的cnt >= 2 那么 就表示这个这个区间的所有长度都是有效长度, sum就等于这个区间的总长度

当cnt == 1时, 表示这整个区间线段至少出现过一次  并且这个区间内的部分线段可能会出现好多次 

这个时候访问这个节点的左子树和右子树的sum2,sum = sum2(左子树)+sum2(右子树)。

因为这个区间的cnt == 1 表示目前这个区间的长度都至少出现过了一次, 由于是区间更新且没有下推cnt标记

如果左子树或右子树上sum2 != 0, 那么表示在左子树或右子树上又出现了一次。

那么子树上的一次+目前区间的一次 就能保证出现两次(及以上)。

(请读者充分理解线段树的区域更新, 如果cnt 上有值的话,那么表示 这个区间被完全覆盖的。 

如果区间A被完全覆盖了, 那么会在区间A对应的cnt++, 然后 进行更新和返回(return;)

但是由于不下推, 所以A的左子树或者右子树上的cnt都不会发生变化。所以,如果A的左子树或右子树上

的cnt不为0,那么一定有另一条线扫描到了左子树或者右子数,并且没有完全覆盖区间A。

所以,如果A的cnt == 1并且 A的子树上有值,那么子树上的线段长度和就是至少访问过2次的线段长度和了);

当然 如果 l==r的时候有效长度还是为0的, 因为叶子节点没有长度。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<iomanip>
 4 #define lson l,m,rt<<1
 5 #define rson m+1,r,rt<<1|1
 6 using namespace std;
 7 const int N = 1e4;
 8 struct Node
 9 {
10     double l, r, h;
11     int d;
12     bool operator < (const Node & x) const
13     {
14         return h < x.h;
15     }
16 }A[N];
17 double X[N], sum[N], sum2[N];
18 int cnt[N];
19 void Build(int l, int r, int rt)
20 {
21     sum2[rt] = 0.0,cnt[rt] = 0, sum[rt] = 0.0;
22     if(l == r) return ;
23     int m = l+r >> 1;
24     Build(lson);
25     Build(rson);
26 }
27 void PushUp(int l, int r, int rt)
28 {
29     if(cnt[rt])
30     {
31         sum2[rt] = X[r] - X[l-1];
32     }
33     else if(l == r) sum2[rt] = 0.0;
34     else sum2[rt] = sum2[rt<<1] + sum2[rt<<1|1];
35     if(cnt[rt] > 1)
36         sum[rt] = X[r] - X[l-1];
37     else if(l == r) sum[rt] = 0;
38     else if(cnt[rt] == 1) sum[rt] = sum2[rt<<1]+sum2[rt<<1|1];
39     else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
40 }
41 void Revise(int L, int R, int C, int l, int r, int rt)
42 {
43     if(L <= l && r <= R)
44     {
45         cnt[rt] += C;
46         PushUp(l,r,rt);
47         return ;
48     }
49     int m = l+r >> 1;
50     if(L <=m) Revise(L,R,C,lson);
51     if(m < R) Revise(L,R,C,rson);
52     PushUp(l,r,rt);
53 }
54 void Add(double l, double r, double h, int d, int i)
55 {
56     A[i].l = l; A[i].h = h;
57     A[i].r = r; A[i].d = d;
58 }
59 int main()
60 {
61     ios::sync_with_stdio(false);
62     cin.tie(0);
63     cout.tie(0);
64     int T, n;
65     cin >> T;
66     while(T-- && cin >> n)
67     {
68         int k = 0;
69         double x1, y1, x2, y2;
70         for(int i = 1; i <= n; i++)
71         {
72             cin >> x1 >> y1 >> x2 >> y2;
73             Add(x1,x2,y1,1,k);
74             X[k++] = x1;
75             Add(x1,x2,y2,-1,k);
76             X[k++] = x2;
77         }
78         sort(X,X+k);
79         sort(A,A+k);
80         int pos = 1;
81         for(int i = 1; i < k; i++)
82         {
83             if(X[i] != X[i-1])
84                 X[pos++] = X[i];
85         }
86         Build(1,pos,1);
87         double ans = 0;
88         for(int i = 0; i < k-1; i++)
89         {
90             int l = lower_bound(X,X+pos,A[i].l) - X;
91             int r = lower_bound(X,X+pos,A[i].r) - X;
92             Revise(l+1,r,A[i].d,1,pos,1);
93             ans += (A[i+1].h - A[i].h) * sum[1];
94         }
95         cout << fixed << setprecision(2) << ans+0.0001 << endl;//莫名其妙样例一的时候没有四舍五入上去
96     }                                  //所以补了一点上去就给过了
97     return 0;
98 }

 

posted @ 2018-01-31 16:59  Schenker  阅读(583)  评论(0编辑  收藏  举报