hdu 1542 Atlantis

题目链接如下:

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

这是一道扫描线和线段树结合的题目。

我们把区间看作子节点,用一个数组存储横坐标,排序去重,就得到了可以维护的区间。

对所有横边按照高排序,我们可以得到每一次扫描线需要更新的顺序。

参考:

https://blog.youkuaiyun.com/u013480600/article/details/22548393

在线段树里面,设置一个cnt用于区间的标记更新,用sum记录区间内有多长的边。

这里说一下cnt=-1的设置,这个-1代表的是其左右区间中存在被上下边覆盖次数不一致的区间。

我一开始觉得,难道cnt不是设置一下总的区间然后直接后续push_down就行了么,但是这样不行,我们每次只对相同覆盖次数的边进行覆盖次数和长度的更新,否则会出现一下提前返回的情况,或者说查询不方便的情况。所以在push_up的时候就会把最近的那个不一样覆盖次数的根节点打上-1的标记。由于所有区间的正常覆盖次数都是大于等于0的,所以-1是可以拿来做标记的。

代码如下所示:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cmath>
#include<climits>

using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

const int MAXN=2222;
struct edge{
    double l,r;
    double h;
    int f;

    edge(){}
    edge(double l,double r,double h,int f):l(l),r(r),h(h),f(f){}
    bool operator<(const edge& e)const{
        return this->h<e.h;
    }
}g[MAXN];

int cnt[MAXN<<2];// 记录是否有边覆盖了这一段区域算作面积 -1表示当前不需要传递cnt
double sum[MAXN<<2];// 记录当前面积长度
double x[MAXN<<1];

// 按照cnt更新sum 同时将cnt的标记传递
// -1表示不用传递
void push_down(int l,int r,int rt){
    int mid=(l+r)>>1;
    if (cnt[rt]!=-1){
//    cout<<"rt:"<<rt<<", cnt:"<<cnt[rt]<<endl;
        cnt[rt<<1]+=cnt[rt];
        cnt[rt<<1|1]+=cnt[rt];
        sum[rt<<1]=(cnt[rt<<1] ? (x[mid+1]-x[l]) : 0);
        sum[rt<<1|1]=(cnt[rt<<1|1] ? (x[r+1]-x[mid+1]) : 0);
        cnt[rt]=0;
    }
}

// 更新sum
void push_up(int rt){
    if (cnt[rt<<1]==-1 || cnt[rt<<1|1]==-1){
        cnt[rt]=-1;
    } else if (cnt[rt<<1]!=cnt[rt<<1|1]){
        cnt[rt]=-1;
    } else{
        cnt[rt]=cnt[rt<<1];
    }
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void build(int l,int r,int rt){
    if (l==r){
        cnt[rt]=0;
        sum[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int a,int b,int val,int l,int r,int rt){
//    cout<<"update a,b,l,r:"<<a<<","<<b<<","<<l<<","<<r<<endl;
    if (a<=l && r<=b){
        if (cnt[rt]!=-1){
            cnt[rt]+=val;
            sum[rt]=cnt[rt] ? (x[r+1]-x[l]) : 0;
//        push_down(l,r,rt);
//        push_up(rt);
            return;
        }
    }
//    cout<<"update rt:"<<rt<<","<<cnt[rt]<<endl;
    push_down(l,r,rt);
    int mid=(l+r)>>1;
    if (a<=mid){
        update(a,b,val,lson);
    }
    if (b>mid){
        update(a,b,val,rson);
    }
    push_up(rt);
}


int main(){
    int num;
    int kase=0;
    double x1,x2,y1,y2;
    while (~scanf("%d",&num) && num){
        int n=0,m=0;
        for (int i = 0; i < num; ++i) {
            scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
            x[++n]=x1;
            g[++m]=edge(x1,x2,y1,1);
            x[++n]=x2;
            g[++m]=edge(x1,x2,y2,-1);
        }
        sort(x+1,x+1+n);
        sort(g+1,g+1+m);
        n=unique(x+1,x+1+n)-x-1;
        build(1,n-1,1);
        double ans=0;
        for (int i = 1; i < m; ++i) {
            int l= upper_bound(x+1,x+1+n,g[i].l)-x-1;
            int r= upper_bound(x+1,x+1+n,g[i].r)-x-2;
//            cout<<"f:"<<g[i].f<<endl;
            update(l,r,g[i].f,1,n-1,1);
//            cout<<"l,r:"<<l<<","<<r<<endl;
//            cout<<sum[1]<<","<<g[i].h<<","<<g[i+1].h<<endl;
//            cout<<sum[2]<<","<<sum[3]<<","<<sum[4]<<","<<sum[5]<<endl;
//            cout<<cnt[2]<<","<<cnt[3]<<","<<cnt[4]<<","<<cnt[5]<<endl;
            ans+=sum[1]*(g[i+1].h-g[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++kase,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值