poj1151-Atlantis(线段树+扫描线(经典题))

题目来源:http://poj.org/problem?id=1151

题意

在二维坐标系上,给出多个矩形的左下以及右上坐标,求出所有矩形构成的图形的面积。

思路

第一次接触扫描线。。。(弱)
好吧,我承认我会了。。。
咳咳,,推荐另外一道题(进行扫描线入门),做完这道题就会知道,一个区间的左端点是1,右端点是-1,那么就不用考虑那么多复杂的情况了。
相应的,在这道题里,假设以和x轴平行的作为扫描线,那么对于矩形而言,也一定有入边,出边。
这里写图片描述
(这图貌似还不错)
然后,线段树作用在什么地方呢?
那么上图:
这里写图片描述
咳咳,这是样例一的模样(花了好长时间),瞅瞅,扫描线把两个矩形组成的图形成功的分成了三部分开始求(从左向右),分别是黄色,红色,和蓝色区域。。
那么右边那些序号是干嘛的,是表明区间的,1~2,2~3之类的,比如:
扫描线经过x=10的时候, 这个时候,发现1~3是一条入边(用1,-1表示,变量名是flag),那么就求出1~3的区间长度=20-10=10,然后通过线段树递归更新到区间1~4里,那么我们就不用每次找在哪哪个区间有多少长度了。
然后,扫描线继续游荡。。。披荆斩棘,,,好不容易到了x=15,然后发现2~3是一条入边,那么立即计算出长度20-15=5,然后赶紧扔给1~4(递归),但是你们看,,除了第一条边(x=10)没有面积,其他边都可以和前面的边构成矩形,。。所以我们要先算出第一条边。。。。
然后到第二条边的时候就可以黄色区域的面积了,面积等于啥呢?
瞅瞅,是(15-10)*length(1~4),对吧,因为刚才所有的长度都递归到区间1~4(线段树的根)了,这样应该可以懂线段是有啥用了把。。。
建树的时候回溯的条件是tree[rt].l+1==tree[rt].r,这里的l和r就是序号,,,挨着就好了,不需要再分成点了,,,。。
然后就没了。。。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct ppx
{
    int l,r;
    double left,right;
    double len;
    int cover;//记录重边情况

} tree[4*200+10];
double y[200+10];//对应的序号装对应的边
struct pp
{
    double x;
    double y1,y2;
    int flag;//标记入边还是出边
} node[200+10];
bool cmp(pp s,pp t)
{
    return s.x<t.x;
}
void build(int rt,int left,int right)
{
    tree[rt].l=left;
    tree[rt].r=right;
    tree[rt].left=y[left];
    tree[rt].right=y[right];
    tree[rt].len=0;
    tree[rt].cover=0;
    if(tree[rt].l+1==tree[rt].r)
    {
        return ;
    }
    int m=(left+right)/2;
    build(rt<<1,left,m);
    build((rt<<1)+1,m,right);
}
void solve(int rt)
{
    if(tree[rt].cover>0)
    {
        tree[rt].len=tree[rt].right-tree[rt].left;//求这段区间长度
    }
   else if(tree[rt].r-tree[rt].l==1)//长度清0
    {
        tree[rt].len=0;
    }
    else
    {
        tree[rt].len=tree[rt<<1].len+tree[(rt<<1)+1].len;//长度递归到线段树的根
    }
}
void updata(int rt,pp b)//写那么复杂为了节省时间
{
    if(b.y1==tree[rt].left&&b.y2==tree[rt].right)//区间查找嘛。。。
    {
        tree[rt].cover+=b.flag;
    }
   else if(b.y2<=tree[rt<<1].right)//找左子树。。。
    {
        updata(rt<<1,b);
    }
    else if(b.y1>=tree[(rt<<1)+1].left)//找右子树。。。。
    {
        updata((rt<<1)+1,b);
    }
    else//分成两段。。。。左子树和右子树,。。。。
    {
        pp temp;
        temp=b;
        temp.y2=tree[rt<<1].right;
        updata(rt<<1,temp);
        temp=b;
        temp.y1=tree[(rt<<1)+1].left;
        updata((rt<<1)+1,temp);
    }
    solve(rt);//区间的长度更新啦
}
int main()
{
    int n,cases=1;
    while(~scanf("%d",&n)&&n)
    {

        int t=1;
        for(int i=1; i<=n; i++)
        {
            scanf("%lf%lf",&node[t].x,&y[t]);
            scanf("%lf%lf",&node[t+1].x,&y[t+1]);
            node[t].y1=y[t];
            node[t].y2=y[t+1];
            node[t+1].y1=y[t];
            node[t+1].y2=y[t+1];
            node[t].flag=1;
            node[t+1].flag=-1;
            t+=2;
        }
        sort(node+1,node+t,cmp);
        sort(y+1,y+t);
        build(1,1,t-1);//为啥t-1,因为现在t比序号大一了。。。因为上面的循环
        updata(1,node[1]);//先算第一条边。。。
        double sum=0;
        for(int i=2; i<t; i++)
        {
            sum+=tree[1].len*(node[i].x-node[i-1].x);//瞅见没,这就是求面积的。。
//            printf("%lf %lf\n",node[i].x-node[i-1].x,tree[1].len);
            updata(1,node[i]);
        }
        printf("Test case #%d\n",cases++);
        printf("Total explored area: %.2f\n\n",sum);
    }
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值