[扫描线+树状数组]解决矩形包含点的问题

  今天做到第二题,大部分的思路都理解了之后最后剩下一个问题

  zzx:“然后扫描线+树状数组搞一下就好了”

  看到这两个算法就产生了一种我肯定会的错觉...

  然后后来发现并不会的时候很惭愧...

  然后十分感谢YDC,在之前完全陌生的情况下 我去问这样一个问题

  超级超级超级耐心地给我解答 

  我问着每一个他们看起来显然的具体处理方法

  突然发现真的像张老师说的:“你普及升提高的这条路没有怎么走,中间那块知识是空着的啊”

  今天才切实体会到...但是或许正是这样今天学到这样的一个东西更让我觉得开心吧

 

 


 

 

  树状数组解决一维点线包含问题

  事实上现在印象非常深刻,初二的时候做过一道苹果的题目,就是运用到树状数组

  也是添加线段,然后查询当前点是否有苹果,当时可能还比较小

  对这样解决方法并不是理解得十分透彻

  1)添加线段(l,r):put(l,1);put(r+1,-1);

  2) 删除线段(l,r):put(l,-1);put(r+1,1);

  3) 横坐标为x的点被覆盖的次数 = get(x);

  其实很好理解...如果把树状数组理解成前缀和一样的东西,相当于x前有多少个左端点,多少个右端点

  然后每一个左端点和一个右端点相互抵消

 

  然后我们拓展到二维的情况

  添加扫描线即可

  对于一系列横坐标为x的点,我们需要在树状数组中的

  是左边界<=x,右边界>x的矩形

  1)我们只需要把还未添加进来的,左边界满足要求的添加进来

  2)把已经添加进来的,右边界不满足要求的删掉即可

  第一种情况显然是满足单调性的,然而第二种实际上也是

  因为右边界都不满足要求了,即<x,那么左边界也一定<x,也就是这个矩形必然添加进来了

  然后对于每一个矩形,其y坐标的区间进行树状数组的删添操作

  然后对于这些点,get(y)就是被覆盖的次数

 

  并没有找到这样的裸题,于是写了个暴力对拍

  需要注意:

  1)坐标往往需要离散化(当然为了偷懒这次就没写离散

  2)一些点或者矩形有必要需要保存id,因为排序之后输出可能会影响答案(在某些题目中

  3)如果题目中并没有明确告诉左上角和右下角的顺序,要记得交换

  4)若边界上的点不算,则需要注意更多的细节

 

 

  1 program sweep;
  2 const maxn = 10010;
  3 var i,n,m,j,k,head,tail:longint;
  4     a,c:array[-1..maxn]of record x1,y1,x2,y2:longint;end;
  5     b:array[-1..maxn]of record x,y,id:longint;end;
  6     f,tr:array[-1..maxn]of longint;
  7 
  8 procedure qsort1(L,R:longint);
  9 var i,j,mid:longint;
 10 begin
 11     i:=L;j:=R;mid:=a[random(R-L+1)+L].x1;
 12     repeat
 13         while (i<R)and(a[i].x1<mid) do inc(i);
 14         while (L<j)and(a[j].x1>mid) do dec(j);
 15         if i<=j then
 16         begin
 17             a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];
 18             inc(i);dec(j);
 19         end;
 20     until i>j;
 21     if i<R then qsort1(i,R);
 22     if L<j then qsort1(L,j);
 23 end;
 24 
 25 procedure qsort2(L,R:longint);
 26 var i,j,mid:longint;
 27 begin
 28     i:=L;j:=R;mid:=b[random(R-L+1)+L].x;
 29     repeat
 30         while (i<R)and(b[i].x<mid) do inc(i);
 31         while (L<j)and(b[j].x>mid) do dec(j);
 32         if i<=j then
 33         begin
 34             b[0]:=b[i];b[i]:=b[j];b[j]:=b[0];
 35             inc(i);dec(j);
 36         end;
 37     until i>j;
 38     if i<R then qsort2(i,R);
 39     if L<j then qsort2(L,j);
 40 end;
 41 
 42 procedure qsort3(L,R:longint);
 43 var i,j,mid:longint;
 44 begin
 45     i:=L;j:=R;mid:=c[random(R-L+1)+L].x2;
 46     repeat
 47         while (i<R)and(c[i].x2<mid) do inc(i);
 48         while (L<j)and(c[j].x2>mid) do dec(j);
 49         if i<=j then
 50         begin
 51             c[0]:=c[i];c[i]:=c[j];c[j]:=c[0];
 52             inc(i);dec(j);
 53         end;
 54     until i>j;
 55     if i<R then qsort3(i,R);
 56     if L<j then qsort3(L,j);
 57 end;
 58 
 59 procedure put(x,y:longint);
 60 begin
 61     while x<=maxn do
 62     begin
 63         inc(tr[x],y);
 64         x:=x+x and (-x);
 65     end;
 66 end;
 67 
 68 function get(x:longint):longint;
 69 begin
 70     get:=0;
 71     while x<>0 do
 72     begin
 73         inc(get,tr[x]);
 74         x:=x-x and (-x);
 75     end;
 76 end;
 77 
 78 begin
 79     assign(input,'sweep.in');reset(input);
 80         assign(output,'sweep.out');rewrite(output);
 81     readln(n,m);
 82     for i:=1 to n do readln(a[i].x1,a[i].y1,a[i].x2,a[i].y2);
 83     //读入矩形左上角和右下角(为了偷懒没有交换23333
 84     for i:=1 to m do begin readln(b[i].x,b[i].y);b[i].id:=i;end;
 85     //读入点坐标
 86     qsort1(1,n);  //按照做边界排序矩形 
 87     qsort2(1,m);
 88     c:=a;qsort3(1,n); //按照右边界排序矩形
 89     head:=1;tail:=1;i:=1;
 90     while i<=m do
 91     begin
 92         while (tail<=n)and(a[tail].x1<=b[i].x) do
 93         begin
 94             put(a[tail].y1,1);
 95             put(a[tail].y2+1,-1);
 96             inc(tail);
 97         end;
 98         //把左边界包含点的矩形添加进来
 99         while (head<=n)and(c[head].x2<b[i].x) do
100         begin
101             put(c[head].y1,-1);
102             put(c[head].y2+1,1);
103             inc(head);
104         end;
105         //把右边界已经跳出点的矩形删去
106         j:=i+1;
107         while (j<=m)and(b[j].x=b[i].x) do inc(j);
108         for k:=i to j-1 do f[b[k].id]:=get(b[k].y);
109         i:=j;
110     end;
111     for i:=1 to m do writeln(f[i]);
112 end.

 

 

 

  #在做题之前多想一些细节总是好的

   

  21/.Aprl

posted @ 2015-04-21 20:40  mjy0724  阅读(708)  评论(0编辑  收藏  举报