[POJ1151]Atlantis(扫描线+线段树)

70 篇文章 0 订阅
13 篇文章 0 订阅

题目描述

传送门

题目大意:给出n个矩形求交的面积。

题解

扫面线的裸题
将纵坐标离散,并且将矩形分解成左右两个边界,按照横坐标排序
线段树的每一个节点[l,r]存储的是dis(l-1~r)这一段离散化之前的距离,维护一个标记表示区间被完全覆盖了几次,每一次修改update就行了
由于扫描线问题的特殊性,不存在在一个大区间里抠去一个小区间的问题,所以这个做法还是非常科学的

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 1005

int T,n,cnt,LSH;double ans;
struct SQU{double a,b,c,d;}squ[N];
struct LINE{double x;int l,r,val;}line[N];
double lsh[N],sum[N*4],len[N*4];int cover[N*4];

void clear()
{
    cnt=LSH=0;ans=0.0;
    memset(lsh,0,sizeof(lsh));memset(sum,0,sizeof(sum));memset(len,0,sizeof(len));memset(cover,0,sizeof(cover));
}
int cmp(LINE a,LINE b)
{
    return a.x<b.x;
}
void update(int now,int l,int r)
{
    if (cover[now]) sum[now]=len[now];
    else if (l==r) sum[now]=0;
    else sum[now]=sum[now<<1]+sum[now<<1|1];
}
void build(int now,int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        len[now]=lsh[l]-lsh[l-1];
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    len[now]=len[now<<1]+len[now<<1|1];
}
void change(int now,int l,int r,int lr,int rr,int v)
{
    if (lr>rr) return;
    int mid=(l+r)>>1;
    if (lr<=l&&r<=rr)
    {
        cover[now]+=v;update(now,l,r);
        return;
    }
    if (lr<=mid) change(now<<1,l,mid,lr,rr,v);
    if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,v);
    update(now,l,r);
}
int main()
{
    while (~scanf("%d",&n))
    {
        if (!n) break;
        clear();
        for (int i=1;i<=n;++i)
        {
            scanf("%lf%lf%lf%lf",&squ[i].a,&squ[i].b,&squ[i].c,&squ[i].d);
            lsh[++LSH]=squ[i].a,lsh[++LSH]=squ[i].c;
        }
        sort(lsh+1,lsh+LSH+1);LSH=unique(lsh+1,lsh+LSH+1)-lsh-1;
        for (int i=1;i<=n;++i)
        {
            int a,c;double b=squ[i].b,d=squ[i].d;
            a=lower_bound(lsh+1,lsh+LSH+1,squ[i].a)-lsh;
            c=lower_bound(lsh+1,lsh+LSH+1,squ[i].c)-lsh;
            line[++cnt].x=b,line[cnt].l=a,line[cnt].r=c,line[cnt].val=1;
            line[++cnt].x=d,line[cnt].l=a,line[cnt].r=c,line[cnt].val=-1;
        }
        sort(line+1,line+cnt+1,cmp);
        build(1,1,LSH);
        for (int i=1,j;i<=cnt;i=j)
        {
            j=i;
            if (i>1) ans+=sum[1]*(line[i].x-line[i-1].x);
            while (line[i].x==line[j].x)
            {
                change(1,1,LSH,line[j].l+1,line[j].r,line[j].val);
                ++j;
            }
        }
        printf("Test case #%d\n",++T);
        printf("Total explored area: %.2lf\n",ans);
        puts("");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值