zoukankan      html  css  js  c++  java
  • 线段树扫描线练习

    概述:

    扫描线是解决坐标系内矩形范围内统计问题的好方法(个人理解不一定对)

    就维护一个区间,就像一根扫描线,沿这个区间垂直的方向遍历一个矩形区域(线动成面^_^)

    遇到要求的答案更新至线段树中,岂不是很优秀。

    应用:

    1.求矩形面积:

       HDU1542

      大意:给几个矩形(有重叠部分),求整体面积。

      解题思路:

      就是很朴素的扫描线想法,将整个图形切割成若干个能够用底乘高求出的形状。

      将输入按照y坐标排序,用线段树维护底长,每进入一条撤销或覆盖操作更新总边长,用总边长乘以高度差更新答案即可^_^

      注意:长度为实数,做一下离散化就好了。

      1 #include<map>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #define lll spc<<1
      6 #define rrr spc<<1|1
      7 struct pct{
      8     double l,r;
      9     double h;
     10     int ll,rr;
     11     int flag;
     12     void ins(double le,double re,double hx,int ll,int rr,int fl)
     13     {
     14         l=le;
     15         r=re;
     16         h=hx;
     17         ll=ll;
     18         rr=rr;
     19         flag=fl;
     20     }
     21 }pc[1000000];
     22 std::map<double,int>M;
     23 int n;
     24 int num[1000000];
     25 double len[1000000];
     26 double rx[1000000];
     27 bool cmp(pct x,pct y)
     28 {
     29     return x.h<y.h;
     30 }
     31 bool dcp(double x,double y)
     32 {
     33     return x<y;
     34 }
     35 namespace Sgt{
     36     void Spushup(int spc,int l,int r)
     37     {
     38         if(num[spc])
     39             len[spc]=rx[r+1]-rx[l];
     40         else if(l==r)
     41             len[spc]=0.00;
     42         else
     43             len[spc]=len[lll]+len[rrr];
     44         return ;
     45     }
     46     void Sbuild(int l,int r,int spc)
     47     {
     48         len[spc]=0.00;
     49         num[spc]=0;
     50         if(l==r)
     51             return ;
     52         int mid=(l+r)>>1;
     53         Sbuild(l,mid,lll);
     54         Sbuild(mid+1,r,rrr);
     55         return ;
     56     }
     57     void Supdate(int ll,int rr,int l,int r,int spc,int cmd)
     58     {
     59         if(ll>r||l>rr)
     60             return ;
     61         if(ll<=l&&r<=rr)
     62         {
     63             num[spc]+=cmd;
     64             Spushup(spc,l,r);
     65             return ;
     66         }
     67         int mid=(l+r)>>1;
     68         Supdate(ll,rr,l,mid,lll,cmd);
     69         Supdate(ll,rr,mid+1,r,rrr,cmd);
     70         Spushup(spc,l,r);
     71     }
     72 }
     73 int main()
     74 {
     75     int t=0;
     76     while(true)
     77     {
     78         scanf("%d",&n);
     79         if(!n)
     80             return 0;
     81         M.clear();
     82         int cnt=0;
     83         double ans=0.00;
     84         int rnt=0;
     85         int dnt=0;
     86         for(int i=1;i<=n;i++)
     87         {
     88             double le,re,hx,hy;
     89             scanf("%lf%lf%lf%lf",&le,&hx,&re,&hy);
     90             pc[++cnt].ins(le,re,hx,0,0,1);
     91             pc[++cnt].ins(le,re,hy,0,0,-1);
     92             rx[++rnt]=le;
     93             rx[++rnt]=re;
     94         }
     95         std::sort(rx+1,rx+rnt+1,dcp);
     96         for(int i=1;i<=rnt;i++)
     97         {
     98             if(M.find(rx[i])==M.end())
     99                 M[rx[i]]=++dnt;
    100             rx[dnt]=rx[i];
    101         }
    102         for(int i=1;i<=cnt;i++)
    103         {
    104             pc[i].ll=M[pc[i].l];
    105             pc[i].rr=M[pc[i].r];
    106         }
    107         std::sort(pc+1,pc+cnt+1,cmp);
    108         Sgt::Sbuild(1,dnt,1);
    109         double btn=0.00;
    110         double lst=0.00;
    111         for(int i=1;i<=cnt;i++)
    112         {
    113             ans+=btn*(pc[i].h-lst);
    114             Sgt::Supdate(pc[i].ll,pc[i].rr-1,1,dnt,1,pc[i].flag);
    115             btn=len[1];
    116             lst=pc[i].h;
    117         }
    118         t++;
    119         printf("Test case #%d
    ",t);
    120         printf("Total explored area: %.2lf
    
    ",ans); 
    121     }
    122     return 0;
    123 }

    2.求矩形周长:

      HDU1828

      大意:给几个矩形(有重叠部分),求整体周长。

      解题思路:

      首先最朴素的扫描线算法就是,先扫一遍横线,再扫一遍竖线,最后统计答案

      也就是说,我们只要统计横线或竖线就好了。

      具体怎么做呢,将输入存为两条平行于扫描线的区间,第一遍叫加入,第二边叫取消,区间查询总长度-上次长度更新答案即可。

      扫两遍嘛,比较麻烦,其实扫一遍是加入区间多少个不连续就好了这个就是竖边的数量再乘以竖边长度即可^_^

      注意一下边的重合先处理顶边就好了。

      代码(怕RE开大了线段树的区间就有点小慢):

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #define lll spc<<1
      5 #define rrr spc<<1|1
      6 typedef long long lnt;
      7 struct trnt{
      8     int nar;
      9     int cl,cr;
     10     int cvr;
     11     int len;
     12     void memd()
     13     {
     14         len=nar=cl=cr=cvr=0;
     15         return ;
     16     }
     17 }tr[1000000];
     18 struct pct{
     19     int l,r;
     20     int h;
     21     int com;
     22 }pc[1000000];
     23 int n;
     24 int cnt;
     25 namespace Sgt{
     26     void Sbuild(int l,int r,int spc)
     27     {
     28         tr[spc].memd();
     29         if(l==r)
     30             return ;
     31         int mid=(l+r)>>1;
     32         Sbuild(l,mid,lll);
     33         Sbuild(mid+1,r,rrr);
     34         return ;
     35     }
     36     void Sdestory()
     37     {
     38         Sbuild(1,40000,1);
     39     }
     40     void Spushup(int spc,int l,int r)
     41     {
     42         if(tr[spc].cvr)
     43         {
     44             tr[spc].nar=1;
     45             tr[spc].len=r-l+1;
     46             tr[spc].cl=tr[spc].cr=1;
     47             return ;
     48         }
     49         if(l==r)
     50         {
     51             tr[spc].memd();
     52             return ;
     53         }
     54         tr[spc].len=tr[lll].len+tr[rrr].len;
     55         tr[spc].nar=tr[lll].nar+tr[rrr].nar;
     56         tr[spc].cl=tr[lll].cl;
     57         tr[spc].cr=tr[rrr].cr;
     58         if(tr[lll].cr&&tr[rrr].cl)
     59             tr[spc].nar--;
     60         return ;
     61     }
     62     void Supdate(int l,int r,int ll,int rr,int spc,int cmd)
     63     {
     64         if(ll>r||l>rr)
     65             return ;
     66         if(ll<=l&&r<=rr)
     67         {
     68             tr[spc].cvr+=cmd;
     69             Spushup(spc,l,r);
     70             return ;
     71         }
     72         int mid=(l+r)>>1;
     73         Supdate(l,mid,ll,rr,lll,cmd);
     74         Supdate(mid+1,r,ll,rr,rrr,cmd);
     75         Spushup(spc,l,r);
     76     }
     77 }
     78 bool cmp(pct x,pct y)
     79 {
     80     if(x.h==y.h)
     81         return x.com>y.com;
     82     return x.h<y.h;
     83 }
     84 int main()
     85 {
     86     while(scanf("%d",&n)!=EOF)
     87     {
     88         lnt ans=0;
     89         cnt=0;
     90         Sgt::Sdestory();
     91         for(int i=1;i<=n;i++)
     92         {
     93             int ll,rr,hhx,hhy;
     94             scanf("%d%d%d%d",&ll,&hhx,&rr,&hhy);
     95             ll+=10005;
     96             rr+=10005;
     97             hhx+=10005;
     98             hhy+=10005;
     99             cnt++;
    100             pc[cnt]=(pct){ll,rr,hhx,1};
    101             cnt++;
    102             pc[cnt]=(pct){ll,rr,hhy,-1};
    103         }
    104         std::sort(pc+1,pc+cnt+1,cmp);
    105         int lst=tr[1].len;
    106         for(int i=1;i<=cnt;i++)
    107         {
    108             Sgt::Supdate(1,30000,pc[i].l,pc[i].r-1,1,pc[i].com);
    109             ans=ans+abs(tr[1].len-lst)+tr[1].nar*2*(pc[i+1].h-pc[i].h);
    110             lst=tr[1].len;
    111         }
    112         printf("%lld
    ",ans);
    113     }
    114     return 0;
    115 }

    3.平面内离散化点的统计:

      这个就是将矩形区域内的答案存起来之后每进一个点查询整个矩形区域更新答案即可。

  • 相关阅读:
    Navicat在MySQL中添加外键详细过程
    java绘图原理------在窗口界面(或面板上)画出一张或多张图片问题解决方法
    记录springboot jar包冲突异常处理
    终止线程
    SpringDataJPA
    mysql安装后无法启动问题
    地理空间几种数据格式
    图幅与经纬度之间的换算
    postgresql12集成postgis与timescale
    C# 微信支付 V2
  • 原文地址:https://www.cnblogs.com/blog-Dr-J/p/9561413.html
Copyright © 2011-2022 走看看