POJ 1177 Picture & HDU 1255 覆盖的面积 【线段树+离散化+扫描线】

题目来源:
POJ http://poj.org/problem?id=1177
HDU :http://acm.hdu.edu.cn/showproblem.php?pid=1255

★实在是蔡,扫描线居然硬是看了一天,就发篇博客说说叭

扫描线:

扫描线一般运用在图形处理上面,来解决一些周长 面积的问题(比如 给你很多矩形的位置,让你求他们总共的面积,注意这里的矩形可以重叠,所以问题就变得复杂了)扫描线思想因运而生?
扫描线顾名思义,就是一条直线(平行于x轴或y轴)在坐标系里面从头到尾扫描检测,具体看下面的例子
现在有2个矩形如下图,如何用扫描线求他们的周长呢?
在这里插入图片描述
我们可以分别用平行于x轴、y轴的直线来扫描这个图形,以平行于y轴的扫描线为例,扫描线的位置不是随便取的,而是恰好和矩形的某边重合,从左到右扫一遍如下

在这里插入图片描述
我们只要把每次(取1 2 3 4时)y轴上(前后有线段覆盖的区域长度差)相加,就可以得到y轴方向的周长了。这中间还会涉及到一些操作,比如把矩形的左边的线段标记为1,右边的标记为-1.
从1开始取,第一次 1覆盖了 加上1的长度 ;
到2了,这时 加上 2覆盖的区域-1覆盖的区域的长度
到3了,这时 1覆盖的区域被3抵消了(标记1 -1 的作用),并且3覆盖的区域为0
最后是4,因为3没有覆盖,直接加上4的
这就是整个过程, 另一半与此类似就不多说了,来看2道例题~

实战:

POJ 1177 Picture

在这里插入图片描述
在这里插入图片描述

思路:

这题就是解释中差不多的做法,2次扫描线,通过线段树来维护就OK了
还有要注意的是 这里不要递归到 l == r 而是 l+1 == r 因为 l == r 在这里没有意义

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e4+5;
const int mod=1e9+7;
typedef long long ll;
int n,m;
int sum[maxn<<2],add[maxn<<2];
struct node
{
    int l,r,h,flag;
}line1[maxn<<1],line2[maxn<<1];
bool cmp(node a,node b)
{
    if(a.h!=b.h) return a.h<b.h;
    return a.flag>b.flag;
}
void build(int k,int l,int r)
{
    sum[k]=0;
    add[k]=0;
    if(l+1==r) return ;
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid,r);
}
void pushup(int k,int l,int r)
{
    if(add[k]){                   
        sum[k]=r-l;
    }
    else if(l+1==r){
        sum[k]=0;
    }
    else{
        sum[k]=sum[k<<1]+sum[k<<1|1];
    }
}
void update(int k,int l,int r,int x,int y,int flag)
{
//    cout<<l<<' '<<r<<endl;
    if(x<=l&&r<=y){
        add[k]+=flag;
        pushup(k,l,r);
        return ;
    }
    int mid=l+r>>1;
    if(mid>x) update(k<<1,l,mid,x,y,flag);
    if(mid<y) update(k<<1|1,mid,r,x,y,flag);
    pushup(k,l,r);
}
int main()
{
    int n;
    while(~scanf("%d",&n)){
        int x1,y1,x2,y2;
        build(1,1,20005);
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x1+=10001; x2+=10001;           //这题有负的坐标,看着怪不习惯的,所以把他们都加上10001,变成正的了
            y1+=10001; y2+=10001;
            line1[i].l=x1; line1[i].r=x2;
            line1[i].h=y1; line1[i].flag=1;

            line1[i+n].l=x1; line1[i+n].r=x2;
            line1[i+n].h=y2; line1[i+n].flag=-1;

            line2[i].l=y1; line2[i].r=y2;
            line2[i].h=x1; line2[i].flag=1;

            line2[i+n].l=y1; line2[i+n].r=y2;
            line2[i+n].h=x2; line2[i+n].flag=-1;
        }
        sort(line1+1,line1+n*2+1,cmp);
        sort(line2+1,line2+n*2+1,cmp);
        int ans=0;
        for(int i=1;i<=2*n;i++){
            int temp=sum[1];
            update(1,1,20005,line1[i].l,line1[i].r,line1[i].flag);
            ans+=abs(sum[1]-temp);
//            cout<<line1[i].l<<' '<<line1[i].r<<' '<<line1[i].h<<' '<<ans<<endl;
        }
        build(1,1,20005);
        for(int i=1;i<=2*n;i++){
            int temp=sum[1];
            update(1,1,20005,line2[i].l,line2[i].r,line2[i].flag);
            ans+=abs(sum[1]-temp);
//            cout<<ans<<endl;
        }
        cout<<ans<<endl;
    }
    return 0;
}
//    1 20 -  4 9
//    1 10     4

HDU 1255 覆盖的面积

借鉴自 https://blog.csdn.net/w326159487/article/details/77714912
在这里插入图片描述
在这里插入图片描述

思路:

首先这题是有小数的,第一步就是离散化,通过STL库里面的unique函数来进行
然后求覆盖的面积,我们想一想,假如只有2个矩形,他们之间有公共的面积的话
还是用上面的图看
在这里插入图片描述
循环到第二条边时,1的区域有了2次覆盖,下一条边的x减去这一条边的x 再乘上这个覆盖区域的长度 就等于覆盖的面积了。我们考虑多个的时候,也可以用这种方法,因为如果有公共面积,一定会出现2次覆盖 以及后面会出现这个存在覆盖区域的矩形的右边。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e3+5;
const int mod=1e9+7;
typedef long long ll;
int n,m;
double sum[maxn<<3],lvl[maxn<<1];
int cover[maxn<<3];
struct node
{
    double x,y1,y2;
    int in;            //记录 左右边的标记
    node(double a,double b,double c,int d) {x = a; y1 = b; y2 = c; in = d;}      //类似于类的构造函数
    node(){}
}f[maxn];
bool cmp(node a,node b){return a.x<b.x;}
int getval(double val) {return lower_bound(lvl+1,lvl+m+1,val)-lvl;}    //二分查找val的位置
void update(int k,int cc,int l,int r,int x,int y)
{
    if(l+1==r){
        cover[k]+=cc;
        sum[k]=cover[k]>1?lvl[r]-lvl[l]:0;
        return ;
    }
    int mid=l+r>>1;
    if(mid>x) update(k<<1,cc,l,mid,x,y);
    if(mid<y) update(k<<1|1,cc,mid,r,x,y);
    sum[k]=sum[k<<1]+sum[k<<1|1];
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(sum,0,sizeof sum);
        memset(cover,0,sizeof cover);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            double a,b,c,d;
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            lvl[i*2-1]=b; lvl[i*2]=d;
            f[i*2-1]=node(a,b,d,1);
            f[i*2]=node(c,b,d,-1);
        }
        sort(f+1,f+2*n+1,cmp);
        sort(lvl+1,lvl+2*n+1);
        m=unique(lvl+1,lvl+2*n+1)-lvl-1;      //去重
        double ans=0;
//        cout<<m<<endl;
        update(1,f[1].in,1,m,getval(f[1].y1),getval(f[1].y2));     //第一次要预先处理,第一次不可能存在重复区域
        for(int i=2;i<=2*n;i++){
            ans+=(f[i].x-f[i-1].x)*sum[1];
//            cout<<sum[1]<<endl;
            update(1,f[i].in,1,m,getval(f[i].y1),getval(f[i].y2));   
        }
        printf("%.2f\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值