• 线段树专辑—— hdu 1542 Atlantis


    http://acm.hdu.edu.cn/showproblem.php?pid=1542

    嗯哼,要开始利用线段树求解矩形面积的并、交、以及周长了。这题是求面积并的

    有两种方法,一种思想很简单,操作方便,理解容易,但效率不高。先看一下吧

    给定一个矩形的左下角坐标和右上角坐标分别为:(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的那些区间,和现在要插入的第二根线段之间,是不是构成了并面积?还不明白?看下图,绿色部分即为插入第二根线段后得到的并面积

    够清楚了吧!也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的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 }

    强烈建议学习第二种方法,通常第一种方法能够的题目,第二种方法都是秒杀!

    该方法同样需要在线段树中定义一个cover域,表示该线段区间目前被覆盖的线段数目。另外再加一个len域,表示该区间可用于与下一线段求并面积的y坐标区间长度。然后利用简单的dp,将所有信息集中于tree[1].len上,这样便不用想第一种方法那样每次都求到叶子线段,大大节约了时间,并且代码也少了很多!

    View Code
      1 #include<iostream>
    2 #include<string>
    3 #include<algorithm>
    4 using namespace std;
    5
    6 struct node
    7 {
    8 int l;
    9 int r;
    10 int cover;
    11 double len;
    12 };
    13
    14 node tree[2000];
    15 double yy[250];
    16 int n,len;
    17
    18 struct Line
    19 {
    20 double y_down;
    21 double y_up;
    22 double x;
    23 int cover;
    24 };
    25
    26 Line line[250];
    27
    28 int cmp(Line a,Line b)
    29 {
    30 return a.x<b.x;
    31 }
    32
    33 int find(double x)
    34 {
    35 int l=0,r=len,mid;
    36 while(l<=r)
    37 {
    38 mid=(l+r)/2;
    39 if(yy[mid]==x)
    40 return mid;
    41 if(yy[mid]<x)
    42 l=mid+1;
    43 else
    44 r=mid-1;
    45 }
    46 return l;
    47 }
    48
    49 void build(int i,int l,int r)
    50 {
    51 tree[i].l=l;
    52 tree[i].r=r;
    53 tree[i].cover=0;
    54 tree[i].len=0;
    55 if(l+1==r)
    56 return;
    57 int mid=(l+r)/2;
    58 build(2*i,l,mid);
    59 build(2*i+1,mid,r);
    60 }
    61
    62 void fun(int i)
    63 {
    64 if(tree[i].cover)
    65 tree[i].len=yy[tree[i].r]-yy[tree[i].l]; //如果cover大于1,那么整段都可用于与下一线段求并面积
    66 else if(tree[i].l+1==tree[i].r) //叶子线段
    67 tree[i].len=0;
    68 else
    69 tree[i].len=tree[2*i].len+tree[2*i+1].len; //很简单的dp
    70 }
    71
    72 void updata(int i,int l,int r,int cover)
    73 {
    74 if(tree[i].l>r || tree[i].r<l)
    75 return;
    76 if(tree[i].l>=l && tree[i].r<=r)
    77 {
    78 tree[i].cover+=cover;
    79 fun(i);
    80 return;
    81 }
    82 updata(2*i,l,r,cover);
    83 updata(2*i+1,l,r,cover);
    84 fun(i);
    85 }
    86
    87 int main()
    88 {
    89 double x1,y1,x2,y2;
    90 int i,m,a,b,cas=1;
    91 freopen("in.txt","r",stdin);
    92 while(scanf("%d",&n)==1 && n)
    93 {
    94 m=0;
    95 for(i=0;i<n;i++)
    96 {
    97 scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
    98 yy[m]=y1;
    99 line[m].cover=1;
    100 line[m].x=x1;
    101 line[m].y_down=y1;
    102 line[m++].y_up=y2;
    103
    104 yy[m]=y2;
    105 line[m].cover=-1;
    106 line[m].x=x2;
    107 line[m].y_down=y1;
    108 line[m++].y_up=y2;
    109 }
    110 sort(yy,yy+m);
    111 len=1;
    112 for(i=1;i<m;i++)
    113 {
    114 if(yy[i-1]!=yy[i])
    115 yy[len++]=yy[i];
    116 }
    117 len--;
    118 build(1,0,len);
    119 sort(line,line+m,cmp);
    120 double ans=0;
    121 printf("Test case #%d\n",cas++);
    122 for(i=0;i<m-1;i++)
    123 {
    124 a=find(line[i].y_down);
    125 b=find(line[i].y_up);
    126 updata(1,a,b,line[i].cover);
    127 ans+=tree[1].len*(line[i+1].x-line[i].x); //tree[1].len已经保留了整个树与line[i+1]所能求并面积的长度
    128 }
    129 printf("Total explored area: %0.2lf\n\n",ans);
    130 }
    131 return 0;
    132 }




  • 相关阅读:
    ListView 分页显示(转载+修改)下
    ListView 分页显示(转载+修改)上
    Android_开发片段(Part 1)
    JSCH执行linux命令
    linux运行wkhtmltopdf
    Apache HttpClient
    JDK自带的URLConnection
    java poi读取excel
    CXF webservice完整例子
    Oracle 常用初始化命令
  • 原文地址:https://www.cnblogs.com/ka200812/p/2247064.html
Copyright © 2020-2023  润新知