扫描线(线段树求多个矩形面积周长)

先放一个大佬的视频链接:
https://www.bilibili.com/video/BV144411Z7tx?p=1
https://www.bilibili.com/video/BV144411Z7tx?p=2
讲得太好了。

下面来说说我理解的扫描线

1.假设有三个如下图的矩形
在这里插入图片描述
2.红线就相当于扫描线,从左至右依次扫过如图示位置
在这里插入图片描述
3.然后扫描线和原矩形的某些横线就构成了新的矩形。
以次来求面积和周长。
在这里插入图片描述

离散化的一些个人理解:(如下图)
上方是真实的边界值//4-30;
下方是离散化后的边界值//1-4;
离散化可节约储存空间,但比较时应该用真实值比较,具体看下面代码。
在这里插入图片描述
Picture POJ - 1177
求多个矩形组成的图形的周长

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e4+10;

//线段树建树存储
//cover 统计出入边,入+1,出-1;
//len 记录区间长度
//num 记录线段数量
struct node
{
    int l,r;
    int cover,len,num;
    bool lf,rf;
}tree[N*8];

//辅助离散化和建树,存储所有y的值。
int y[N];

//扫描线存储
//x扫描线的位置,abs(y1-y2)扫描线长度
struct L
{
    int x,y1,y2,id;//id为1为入边,id为-1为出边
}line[N];
bool cmp(L a,L b)
{
    if(a.x==b.x)return a.id>b.id;
    else return a.x<b.x;
}
void pushup(int node)
{
    if(tree[node].cover)
    {
        tree[node].len=y[tree[node].r]-y[tree[node].l];
        tree[node].num=1;
        tree[node].lf=tree[node].rf=true;
    }
    else if(tree[node].l+1==tree[node].r)
    {
        tree[node].lf=tree[node].rf=false;
        tree[node].num=tree[node].len=0;
    }
    else
    {
        tree[node].lf=tree[node*2].lf;
        tree[node].rf=tree[node*2+1].rf;
        tree[node].len=tree[node*2].len+tree[node*2+1].len;
        tree[node].num=tree[node*2].num+tree[node*2+1].num;
        if(tree[node*2].rf&&tree[node*2+1].lf)tree[node].num--;
    }
}

//建树比较特殊
//常规建树是(l,mid)(mid+1,r)这里(1,mid),(mid,r)
//因为未离散化是连续的(不用考虑(mid,mid+1))
//离散化后可能是不连续的(需要考虑(mid,mid+1))
void build(int node,int l,int r)
{
    tree[node].l=l;tree[node].r=r;
    tree[node].num=0;
    if(l+1>=r)return;
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid,r);
}
void update(int node,int a,int b,int ff)
{
    if(a<=y[tree[node].l]&&b>=y[tree[node].r])
    {
        tree[node].cover+=ff;
        pushup(node);
        return;
    }
    //a是y[..]比
    //可以结构体里多定义两个l,r储存真实的边界。
    if(a<y[tree[node*2].r])update(node*2,a,b,ff);
    if(b>y[tree[node*2+1].l])update(node*2+1,a,b,ff);
    pushup(node);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        y[i]=y1;y[i+n]=y2;
        line[i].y1=y1;line[i].y2=y2;
        line[i].x=x1;line[i].id=1;
        line[i+n].y1=y1;line[i+n].y2=y2;
        line[i+n].x=x2;line[i+n].id=-1;
    }
    sort(y+1,y+1+2*n);
    sort(line+1,line+1+2*n,cmp);
    int m=unique(y+1,y+1+2*n)-(y+1);
    build(1,1,m);
    int ans=0,lines=0,last=0;
    for(int i=1;i<=2*n;i++)
    {
        update(1,line[i].y1,line[i].y2,line[i].id);
        ans+=2*lines*(line[i].x-line[i-1].x);
        ans+=abs(last-tree[1].len);
        last=tree[1].len;
        lines=tree[1].num;
    }
    printf("%d\n",ans);
    return 0;
}

https://www.luogu.com.cn/problem/P5490
求多个矩形的面积

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
struct node
{
    int l,r,len,cover;
}tree[N*8];
int y[N];
struct L
{
    int x,y1,y2,id;
    bool operator<(L oth)const{return x < oth.x;}
}line[N];
void build(int node,int l,int r)
{
    tree[node].l=y[l];tree[node].r=y[r];
    if(l+1>=r)return;
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid,r);
}
void pushup(int node)
{
    if(tree[node].cover)tree[node].len=tree[node].r-tree[node].l;
    else tree[node].len=tree[node*2].len+tree[node*2+1].len;
}
void update(int a,int b,int c,int node)
{
    if(a<=tree[node].l&&b>=tree[node].r)
    {
        tree[node].cover+=c;
        pushup(node);
        return;
    }
    if(a<tree[node*2].r)update(a,b,c,node*2);
    if(b>tree[node*2+1].l)update(a,b,c,node*2+1);
    pushup(node);
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        y[i]=y1;y[i+n]=y2;
        line[i].x=x1;line[i].id=1;
        line[i].y1=y1;line[i].y2=y2;
        line[i+n].x=x2;line[i+n].id=-1;
        line[i+n].y1=y1;line[i+n].y2=y2;
    }
    sort(y+1,y+1+2*n);
    sort(line+1,line+1+2*n);
    build(1,1,n*2);
    unsigned ll ans=0;
    for(int i=1;i<=2*n;i++)
    {
        //这里记得long long 的转化,就是乘1LL;
        ans+=tree[1].len*1LL*(line[i].x-line[i-1].x);
        update(line[i].y1,line[i].y2,line[i].id,1);
    }
    printf("%llu\n",ans);
    return 0;
}

覆盖的面积 HDU - 1255
题意:求至少被覆盖两次的面积。
思路:相比上面求面积,多加一个len2保存被覆盖两次的长度。


const int N=2010;
struct node
{
    double l,r,len1,len2;
    int cover;
}tree[N*8];
double y[N];
struct L
{
    double x,y1,y2;
    int id;
}line[N];
bool cmp(L a,L b)
{
    if(a.x==b.x)return a.id>b.id;
    else return a.x<b.x;
}
void build(int node,int l,int r)
{
    tree[node].l=y[l];tree[node].r=y[r];
    //printf("%.2lf  %.2lf\n",tree[node].l,tree[node].r);
    if(l+1>=r)return;
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid,r);
}
void pushup(int node)
{
    if(tree[node].cover>=2)
    {
        tree[node].len2=tree[node].r-tree[node].l;
        tree[node].len1=tree[node].r-tree[node].l;
    }
    else if(tree[node].cover>=1)
    {
        //注意len2,此时被完全覆盖一次,所以len2等于上次已覆盖的len1相加。
        tree[node].len2=tree[node*2].len1+tree[node*2+1].len1;
        tree[node].len1=tree[node].r-tree[node].l;
    }
    else
    {
        tree[node].len1=tree[node*2].len1+tree[node*2+1].len1;
        tree[node].len2=tree[node*2].len2+tree[node*2+1].len2;
    }
}
void update(double a,double b,int c,int node)
{
    if(a<=tree[node].l&&b>=tree[node].r)
    {
        tree[node].cover+=c;
       // printf("%d\n",tree[node].cover);
        pushup(node);
        return;
    }
    if(a<tree[node*2].r)update(a,b,c,node*2);
    if(b>tree[node*2+1].l)update(a,b,c,node*2+1);
    pushup(node);
}
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[i]=y1;y[i+n]=y2;
            line[i].x=x1;line[i].id=1;
            line[i].y1=y1;line[i].y2=y2;
            line[i+n].x=x2;line[i+n].id=-1;
            line[i+n].y1=y1;line[i+n].y2=y2;
        }
        sort(y+1,y+1+2*n);
        sort(line+1,line+1+2*n,cmp);
        //for(int i=1;i<=n*2;i++)printf("%.2lf ",y[i]);
        build(1,1,2*n);
        double ans=0;
        for(int i=1;i<=n*2;i++)
        {
            //printf("%.2lf  %.2lf  %.2lf\n",ans,tree[1].len2,line[i].x);
            ans+=tree[1].len2*(line[i].x-line[i-1].x);
            update(line[i].y1,line[i].y2,line[i].id,1);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}

Atlantis HDU - 1542
简单求面积


const int N=210;
struct node
{
    double l,r,len1;
    int cover;
}tree[N*8];
double y[N];
struct L
{
    double x,y1,y2;
    int id;
}line[N];
bool cmp(L a,L b)
{
    if(a.x==b.x)return a.id>b.id;
    else return a.x<b.x;
}
void build(int node,int l,int r)
{
    tree[node].l=y[l];tree[node].r=y[r];
    //printf("%.2lf  %.2lf\n",tree[node].l,tree[node].r);
    if(l+1>=r)return;
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid,r);
}
void pushup(int node)
{
    if(tree[node].cover)tree[node].len1=tree[node].r-tree[node].l;
    else tree[node].len1=tree[node*2].len1+tree[node*2+1].len1;

}
void update(double a,double b,int c,int node)
{
    if(a<=tree[node].l&&b>=tree[node].r)
    {
        tree[node].cover+=c;
        pushup(node);
        return;
    }
    if(a<tree[node*2].r)update(a,b,c,node*2);
    if(b>tree[node*2+1].l)update(a,b,c,node*2+1);
    pushup(node);
}
int main()
{
    int cnt=1,n;
    while(~scanf("%d",&n)&&n)
    {
        for(int i=1;i<=n;i++)
        {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[i]=y1;y[i+n]=y2;
            line[i].x=x1;line[i].id=1;
            line[i].y1=y1;line[i].y2=y2;
            line[i+n].x=x2;line[i+n].id=-1;
            line[i+n].y1=y1;line[i+n].y2=y2;
        }
        sort(y+1,y+1+2*n);
        sort(line+1,line+1+2*n,cmp);
        build(1,1,2*n);
        double ans=0;
        for(int i=1;i<=n*2;i++)
        {
            ans+=tree[1].len1*(line[i].x-line[i-1].x);
            update(line[i].y1,line[i].y2,line[i].id,1);
        }
        printf("Test case #%d\n",cnt++);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值