线段树扫描线求面积交和并、周长 [POJ 1177] [HDU 1255] [HDU 1542]

本文介绍如何使用扫描线算法结合线段树解决二维平面上的矩形面积之交、面积之并及周长等问题,并提供详细的算法实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天来谈一谈线段树扫描线求相关的二维平面上多个矩形的面积之交、面积之并、周长等相关问题。

首先,有几道模板题目:

http://poj.org/problem?id=1177 [求周长]

http://acm.hdu.edu.cn/showproblem.php?pid=1255 [求面积之交]

http://acm.hdu.edu.cn/showproblem.php?pid=1542 [求面积之并]

这几道是扫描线的入门题目,也是裸的模板题目。(看题解时最好一边在纸画一下)

我的做法是先存上、下底边(线段),上底边标记是-1,下底边标记是1,枚举每一条线段“扫描线从下到上扫描”,将线段离散化成区间问题,用线段树存起来,变成询问区间求和(线段总和)问题。

如果是枚举到标记是1的边,那么在Update过程中要不断在相应的区间+1;相反如果是-1,那么久就-1,对枚举的每一条边都要Update一次。

其实三道题的模板都是一样的,主要在修改一下PushUp函数,因为原理是差不多的。

求周长:获得当前枚举线段后的总区间总长度len,同时要得到平行于y轴的线段条数,相应的乘上系数同时要加上“底边”长度(len -上一条边枚举后获得的“底边”长度),然后就没有然后了。

求面积之并:在Update后获得“底边”长度len后,乘上上一条边和这条边的距离就是当前枚举获得的面积。具体操作在PushUp修改。

求面积之交: 同样是在操作获得“底边”长度,只是在标记过程中要多加个判断,也就是覆盖次数大于1的才是我们想要的,得到之后的操作跟求面积并一样了。下面我分享一下自己“漂亮”的代码。【师兄说:写代码要跟写诗一样^_^

求周长:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 10000+10;
int x[N];

struct Line{
    int l,r,h,f;
}line[N];

struct Tree{
    int len,num,cover;
    bool lf,rf;
}tree[N<<2];

bool cmp(Line A,Line B){
    return A.h<B.h;
}
void PushUp(int root,int l,int r){
    if(tree[root].cover>0){
        tree[root] = Tree{x[r+1]-x[l],1,tree[root].cover,1,1};
        return ;
    }
    if(l==r){
        tree[root] = Tree{0,0,0,0,0,};
        return;
    }
    tree[root].lf = tree[root<<1].lf;
    tree[root].rf = tree[root<<1|1].rf;
    tree[root].len = tree[root<<1].len+tree[root<<1|1].len;
    tree[root].num = tree[root<<1].num+tree[root<<1|1].num - (tree[root<<1].rf & tree[root<<1|1].lf);
}
void Update(int root,int l,int r,int ql,int qr,int f){
    if(ql<=l && r<=qr){
        tree[root].cover += f;
        PushUp(root,l,r);
        return ;
    }
    int mid = (l+r)>>1;
    if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
    if(mid<qr) Update(root<<1|1,mid+1,r,ql,qr,f);
    PushUp(root,l,r);
}
int main(void){
   // freopen("c.txt","r",stdin);
    int n,x1,x2,y1,y2;
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            line[i] = Line{x1,x2,y1,1};
            line[i+n] = Line{x1,x2,y2,-1};
            x[i] = x1,x[i+n] = x2;
        }
        n <<= 1;
        sort(x+1,x+1+n);
        int tot = unique(x+1,x+1+n)-x-1;
        sort(line+1,line+n+1,cmp);
        int ans = 0;
        int pre_num=0,pre_len=0;
        for(int i=1;i<=n;i++){
            int ql = lower_bound(x+1,x+1+tot,line[i].l)-x;
            int qr = lower_bound(x+1,x+1+tot,line[i].r)-x-1;
            Update(1,1,tot,ql,qr,line[i].f);
            if(i>=2){
                ans += pre_num * 2 * (line[i].h - line[i-1].h);
            }
            ans += abs(tree[1].len - pre_len);
            pre_num = tree[1].num;
            pre_len = tree[1].len;
        }
        printf("%d\n",ans);
    }
    return 0;
}

求面积之并:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 250;
double sum[N<<2],len[N<<2],x[N<<1];
struct Node{
    double l,r,h;
    int f;
}node[N<<1];
bool cmp(Node A,Node B){
    return A.h<B.h;
}
void PushUp(int root,int l,int r){
    if(len[root]) sum[root] = x[r+1] - x[l];
    else if(l==r) sum[root] = 0;
    else sum[root] = sum[root<<1] + sum[root<<1|1];
}
void Update(int root,int l,int r,int ql,int qr,int f){
    if(ql<=l && r<=qr){
        len[root] += f;
        PushUp(root,l,r);
        return ;
    }
    int mid = (l+r)>>1;
    if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
    if(mid<qr) Update(root<<1 | 1,mid+1,r,ql,qr,f);
    PushUp(root,l,r);
}
int main(void){
    int n,cas = 1;
   //freopen("c.txt","r",stdin);
    double x1,x2,y1,y2;
    while(~scanf("%d",&n) && n){
        memset(sum,0,sizeof(sum));
        memset(len,0,sizeof(len));
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            node[i] = Node{x1,x2,y1,1};
            node[i+n] = Node{x1,x2,y2,-1};
            x[i] = x1,x[i+n] = x2;
        }
        n<<=1;
        sort(x+1,x+n+1);
        int tot = unique(x+1,x+n+1)-x-1;
        sort(node+1,node+1+n,cmp);
        double ans = 0;
        for(int i=1;i<=n;i++){
            int ql = lower_bound(x+1,x+tot+1,node[i].l)-x;
            int qr = lower_bound(x+1,x+tot+1,node[i].r)-x-1;
            Update(1,1,tot,ql,qr,node[i].f);
            ans += sum[1]*(node[i+1].h-node[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ans);
    }
    return 0;
}

求面积之交:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 2000+10;
int cover[N<<2];
double len[N<<2],once[N<<2],x[N];

struct Node{
    double l,r,h;
    int f;
}node[N];
bool cmp(Node A,Node B){
    if(A.h==B.h) return A.f>B.f;
    return A.h<B.h;
}
void PushUp(int root,int l,int r){
    if(cover[root]>=2){
        once[root] = 0;
        len[root] = x[r+1]-x[l];
    }
    else if(cover[root]==1){
        if(l==r) len[root] = 0;
        else{
            len[root] = len[root<<1] + len[root<<1|1] + once[root<<1] + once[root<<1|1];
        }
        once[root] = x[r+1]-x[l]-len[root];
    }
    else if(cover[root]==0){
        if(l==r) len[root] = once[root] = 0;
        else{
            len[root] = len[root<<1] + len[root<<1|1];
            once[root] = once[root<<1] + once[root<<1|1];
        }
    }

}
void Update(int root,int l,int r,int ql,int qr,int f){
    if(ql<=l && r<=qr){
        cover[root] += f;
        PushUp(root,l,r);
        return ;
    }
    int mid = (l+r)>>1;
    if(ql<=mid) Update(root<<1,l,mid,ql,qr,f);
    if(mid<qr) Update(root<<1|1,mid+1,r,ql,qr,f);
    PushUp(root,l,r);
}

int main(void){
    int T;
//    freopen("c.txt","r",stdin);
    double x1,x2,y1,y2;
    scanf("%d",&T);
    while(T--){
        int n;
        memset(len,0,sizeof(len));
        memset(once,0,sizeof(once));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            node[i] = Node{x1,x2,y1,1};
            node[i+n] = Node{x1,x2,y2,-1};
            x[i] = x1,x[i+n]=x2;
        }
        n <<= 1;
        sort(x+1,x+n+1);
        int tot = unique(x+1,x+n+1)-x-1;
        sort(node+1,node+n+1,cmp);
        double ans = 0;
        for(int i=1;i<=n;i++){
            int ql = lower_bound(x+1,x+1+tot,node[i].l)-x;
            int qr = lower_bound(x+1,x+1+tot,node[i].r)-x-1;
            Update(1,1,tot,ql,qr,node[i].f);
            ans += len[1]*(node[i+1].h-node[i].h);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值