扫描线小整理 HDU1542 HDU1255

以前一直没仔细学一波扫描线,最近终于补了一下。

算法描述

大致的思想:将坐标的一维离散化,然后建立线段树,维护当前被覆盖的线段长度,然后将另一维排序,按顺序插入线段树。对于一个矩形的上底边和下底边,(如果从下往上扫描)则对下底边作+1标记,上底边作-1标记。用一个结构体存储一条边的信息,包括L:线段左端点,R:线段右端点,h:线段的高度,d:线段的上下底边标记。线段树的一个细节是,每个节点代表的是第几段线段,而不是某个线段的端点,所以线段[L,R]代表的是第L段线段到第R段线段,
其真实长度=v[R+1]-v[L];(其中v数组是离散化数组) 每次两线段之间的面积就是两线段高度差*x轴方向覆盖的线段长度。

代码

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define ll long long int
#define INF 0x3f3f3f3f
int n,m;
struct node{
    double l,r,h;
    int d;
    bool operator < (const node& rid)const {
        return h < rid.h;
    }
};
node a[maxn<<1];
vector<double>v;
int tot;
int cnt[maxn<<2];
double sum[maxn<<2];
void pushup(int x,int l,int r)
{
    if(cnt[x])sum[x]=v[r+1]-v[l];
    else if(l==r)sum[x]=0;
    else sum[x]=sum[x<<1]+sum[x<<1|1];
}
void update(int x,int l,int r,int lp,int rp,int s){
    if(lp<=l&&r<=rp){
        cnt[x]+=s;
        pushup(x,l,r);
        return ;
    }
    int mid=(l+r)>>1;
    if(lp<=mid)update(x<<1,l,mid,lp,rp,s);
    if(mid+1<=rp)update(x<<1|1,mid+1,r,lp,rp,s);
    pushup(x,l,r);
}
int main()
{
    scanf("%d",&n);
    v.push_back(-1.0*INF);///保证后来插入的数字最小id为1
    for(int i=1;i<=n;i++){
        double x1,x2,y1,y2;
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
        a[++tot]={x1,x2,y1,1};
        a[++tot]={x1,x2,y2,-1};
        v.push_back(x1);
        v.push_back(x2);
    }
    sort(v.begin(),v.end());
    sort(a+1,a+tot+1);
    int len=unique(v.begin(),v.end())-v.begin();
    double ans=0;
    for(int i=1;i<=tot;i++){
        int xx=lower_bound(v.begin(),v.begin()+len,a[i].l)-v.begin();
        int yy=lower_bound(v.begin(),v.begin()+len,a[i].r)-v.begin();
        yy--;
        int dd=a[i].d;
        if(xx<=yy)update(1,1,len-1,xx,yy,dd);
        ans+=sum[1]*(a[i+1].h-a[i].h);///因为最后一条上边延被减去 所以实际上底边为0 所以即使a[i+1]不存在 也不会导致问题
    }
    printf("%lf\n",ans);
    return 0;
}

HDU1542 Atlantis

模板题,给定N个矩形,求覆盖的总面积。注意格式,每次输出之后要跟一个换行。

#include<bits/stdc++.h>
using namespace std;
#define maxn 205
#define ll long long int
#define INF 0x3f3f3f3f
int n;
struct node{
    double l,r,h;
    int d;
    bool operator < (const node &rid)const{
        return h < rid.h;
    }
}a[maxn<<1];
int tot;
vector<double>v;
int cnt[maxn<<2];
double sum[maxn<<2];
void pushup(int x,int l,int r){
    if(cnt[x])sum[x]=v[r+1]-v[l];
    else if(l==r)sum[x]=0;
    else sum[x]=sum[x<<1]+sum[x<<1|1];
}
void update(int x,int l,int r,int lp,int rp,int s)
{
    if(lp<=l&&r<=rp){
        cnt[x]+=s;
        pushup(x,l,r);
        return ;
    }
    int mid=(l+r)>>1;
    if(lp<=mid)update(x<<1,l,mid,lp,rp,s);
    if(mid+1<=rp)update(x<<1|1,mid+1,r,lp,rp,s);
    pushup(x,l,r);
}
int main()
{
    int kase=0;
    while(~scanf("%d",&n)){
        if(n==0)break;
        tot=0;
        v.clear();
        v.push_back(-1.0*INF);
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++){
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            a[++tot]={x1,x2,y1,1};
            a[++tot]={x1,x2,y2,-1};
            v.push_back(x1);
            v.push_back(x2);
        }
        sort(v.begin(),v.end());
        sort(a+1,a+tot+1);
        int len=unique(v.begin(),v.end())-v.begin();
        double ans=0;
        for(int i=1;i<=tot;i++){
            int xx=lower_bound(v.begin(),v.begin()+len,a[i].l)-v.begin();
            int yy=lower_bound(v.begin(),v.begin()+len,a[i].r)-v.begin();
            yy--;
            int dd=a[i].d;
            update(1,1,len-1,xx,yy,dd);
            ans+=sum[1]*(a[i+1].h-a[i].h);
        }
        printf("Test case #%d\n",++kase);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}

HDU 1255 覆盖的面积

题意如图:
在这里插入图片描述这题的难点在于,覆盖两次,不能简单理解为之前的pushup中的cnt[x]>=2即可,因为即使我这次该区间只被覆盖一次,但是由于底下的小区间未被更新,之前就存在的被覆盖一次的现在会变为两次,所以如果现在该区间被覆盖一次之后,sum[x][1]=sum[x<<1][1]+sum[x<<1|1][1]+sum[x<<1][0]+sum[x<<1|1][0];其中sum[x][1]表示被覆盖两次及其以上的区间的长度,sum[x][0]表示被覆盖一次的区间的长度。
然后对于sum[x][0]的更新也要仔细讨论。详细见代码。

///覆盖两次以上的面积
#include<bits/stdc++.h>
using namespace std;
#define maxn 2005
#define ll long long int
#define INF 0x3f3f3f3f
int n,t;
struct node{
    double l,r,h;
    int d;
    bool operator < (const node &rid)const{
        return h < rid.h;
    }
}a[maxn<<1];
int tot;
vector<double>v;
int cnt[maxn<<2];
double sum[maxn<<2][2];
void pushup(int x,int l,int r){
    if(cnt[x]>=2){
        sum[x][1]=v[r+1]-v[l];
        sum[x][0]=0;
    }
    else {
        if(cnt[x]){
            sum[x][1]=sum[x<<1][0]+sum[x<<1|1][0]+sum[x<<1][1]+sum[x<<1|1][1];
            sum[x][0]=v[r+1]-v[l]-sum[x][1];
        }
        else {
            if(l==r)sum[x][1]=sum[x][0]=0;
            else {
                sum[x][1]=sum[x<<1][1]+sum[x<<1|1][1];
                sum[x][0]=sum[x<<1][0]+sum[x<<1|1][0];
            }
        }
    }
}
void update(int x,int l,int r,int lp,int rp,int s){
    if(lp<=l&&r<=rp){
        cnt[x]+=s;
        pushup(x,l,r);
        return ;
    }
    int mid=(l+r)>>1;
    if(lp<=mid)update(x<<1,l,mid,lp,rp,s);
    if(mid+1<=rp)update(x<<1|1,mid+1,r,lp,rp,s);
    pushup(x,l,r);
}
int main()
{
    scanf("%d",&t);
    while(t--){
        tot=0;
        v.clear();
        v.push_back(-1.0*INF);
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            a[++tot]={x1,x2,y1,1};
            a[++tot]={x1,x2,y2,-1};
            v.push_back(x1);
            v.push_back(x2);
        }
        sort(v.begin(),v.end());
        sort(a+1,a+tot+1);
        int len=unique(v.begin(),v.end())-v.begin();
        double ans=0;
        for(int i=1;i<=tot;i++){
            int xx=lower_bound(v.begin(),v.begin()+len,a[i].l)-v.begin();
            int yy=lower_bound(v.begin(),v.begin()+len,a[i].r)-v.begin();
            yy--;
            int dd=a[i].d;
            update(1,1,len-1,xx,yy,dd);
            ans+=sum[1][1]*(a[i+1].h-a[i].h);
            //printf("%lf\n",ans);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值