线段树扫描线总结(POJ 1389)

扫描线算是线段树的一个比较特殊的用法,虽然NOIP不一定会考,但是学学还是有用的,况且也不是很难理解。

以前学过一点,不是很透,今天算是搞懂了。

就以这道题为例吧:嘟嘟嘟

题目的意思是在一个二维坐标系中给了很多矩形,然后求这些矩形的总覆盖面积,也就是面积并。

我就不讲暴力,直接切入正题吧。

 

扫描线,听这个名字就可以想象一下,现在有这么多重叠的矩形,然后有一个线从下往上扫,那么每一时刻这条线上被覆盖的长度之和,就是我们要求的答案。

那么首先可以想到,要把给定的矩形都离线下来,拆成上下来个面,并标记这个矩形从哪开始,从哪结束,最后按x(或y)排好序。

上面的可以算作预处理,接下来才是重点:怎么用线段树维护这个扫描线的覆盖问题?

首先要清楚的是,这并不是单纯的区间覆盖,因为区间覆盖的清空是把这个区间都清零,但是在对于扫线上的一段区间的清空,实际上只是清空了这一层,而他下面那一层应该还保持原样。这该怎么实现呢? 

首先需要一个cov标记,表示这个区间被覆盖了几层,那么一个矩形的开始就是多覆盖一层,结束就是减去一层。由此可知,如果cov > 1的话,那么这个区间就全被覆盖了,sum就等于区间长度;那么cov = 0呢?说明这个区间只有一部分被覆盖,sum[now] = sum[now <<1] +sum[now <<1 | 1]。这是为什么就等于左右子区间的和呢?想一下,我们更新的时候,要是更新区间和当前区间一样的话,就停止了,那么他的子区间还是保持了原来的情况。因此我们掀开了这一层,露出来的就是他的子区间那没有被完全覆盖的情况。因此sum[now] = sum[now << 1] +sum[now <<1 | 1].

还有一点,那就是cov永远不会小于0,因为cov--的条件是有一个矩形结束了,那有结束必定有开始,所以一定是先加后减。但有人又会问,如果是别的矩形改变了这个区间的cov值呢?那也同理,也是先加后减。所以cov永远是大于0的。

pushup也一样,cov大于0,则sum就是区间长度,否则是左右子区间的和。

(懒得画图……)

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 5e4 + 5;
 21 inline ll read()
 22 {
 23   ll ans = 0;
 24   char ch = getchar(), last = ' ';
 25   while(!isdigit(ch)) {last = ch; ch = getchar();}
 26   while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27   if(last == '-') ans = -ans;
 28   return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32   if(x < 0) x = -x, putchar('-');
 33   if(x >= 10) write(x / 10);
 34   putchar(x % 10 + '0');
 35 }
 36 
 37 int xl, yl, xr, yr, cnt = 1;
 38 struct Node
 39 {
 40   int L, R, h, flg;
 41   bool operator < (const Node &oth)const
 42   {
 43     return h < oth.h;
 44   }
 45 }a[maxn << 1];
 46 
 47 int l[maxn << 2], r[maxn << 2], sum[maxn << 2], cov[maxn << 2];
 48 void build(int L, int R, int now)
 49 {
 50   sum[now] = cov[now] = 0;
 51   l[now] = L; r[now] = R;
 52   if(L == R) return;
 53   int mid = (L + R) >> 1;
 54   build(L, mid, now << 1);
 55   build(mid + 1, R, now << 1 | 1);
 56 }
 57 void update(int L, int R, int flg, int now)
 58 {
 59   if(L == l[now] && R == r[now])
 60     {
 61       cov[now] += flg;
 62       if(cov[now]) sum[now] = R - L + 1;
 63       else
 64     {
 65       if(L == R) sum[now] = 0;        //别忘判断叶子节点 
 66       else sum[now] = sum[now << 1] + sum[now << 1 | 1];
 67     }
 68       return;
 69     }
 70   int mid = (l[now] + r[now]) >> 1;
 71   if(R <= mid) update(L, R, flg, now << 1);
 72   else if(L > mid) update(L, R, flg, now << 1 | 1);
 73   else update(L, mid, flg, now << 1), update(mid + 1, R, flg, now << 1 | 1);
 74   if(cov[now]) sum[now] = r[now] - l[now] + 1;
 75   else sum[now] = sum[now << 1] + sum[now << 1 | 1];
 76 }
 77 
 78 int solve()
 79 {
 80   build(1, maxn - 1, 1);
 81   ll ans = 0;
 82   sort(a + 1, a + cnt + 1);
 83   for(int i = 1; i <= cnt; ++i)
 84     {
 85       update(a[i].L, a[i].R, a[i].flg, 1);
 86       ans += sum[1] * (a[i + 1].h - a[i].h);
 87     }
 88   return ans;
 89 }
 90 
 91 int main()
 92 {
 93   while(1)
 94     {
 95       cnt = 0;
 96       bool flg = 0;
 97       while(1)
 98     {
 99       xl = read() + 1; yl = read() + 1; xr = read(); yr = read() + 1;
100       if(!xl && !flg) return 0;
101       else if(!xl) break;
102       else
103         {
104           a[++cnt] = (Node){xl, xr, yl, 1};
105           a[++cnt] = (Node){xl, xr, yr, -1};
106           flg = 1;
107         }
108     }
109       write(solve()); enter;
110     }
111   return 0;
112 }
View Code

 

posted @ 2018-10-12 23:59  mrclr  阅读(304)  评论(0编辑  收藏  举报