离散化-线段树-扫描线小结

昨天打了南宁赛区的网络赛,碰到一个求矩形并的面积的模板题,然而没A出来(不会啊哭)。以前也碰到过类似线段树+扫描线的题目,只能怪自己以前没补题吧哎~。

关于这个知识点,网上随便一搜就能搜到大堆,就说一下我在学习的过程中碰到的问题。

一:首先是离散化,定义就不说了(其实不知道2333),我笼统地理解就是把大的数据范围缩小,同时又保证数据之间地关系不变,比如说坐标的大小,如果给你一些点,你需要的属性只是点与点之间的距离(与各个点的具体坐标值无关),那么我们可以把点的坐标映射到一个比较小的范围,然后把原来的属性放到新的坐标上就好,一个最简单的例子:对于点(-1e8,0),(1e8,0)之间的距离,如果要求的是两个点之间的水平距离,那么我们把-1e8映射到1,1e8映射到2,那么距离就是1到2的距离。这只是一个很笼统的说法,总之我理解的就是把很多点压缩到一起,同时记录点与点之间的属性,当你询问点与点之间的关系时,就可以直接通过查询新的点得到。

回归主题,这里的离散化的作用也是化大坐标为小坐标(我这里自下而上扫),方便建树,比如原来最大范围-10000~10000的线段,而两个值对应的映射为1~100,那么我们就只要建立[1~100]的线段树,对于每一个区间的实际长度,再根据左右两个端点对应的值计算。那么如何保证能通过映射能相互查询到呢--把原数据排序并去重,然后记每一个数的映射值就是下标,那么我们当要知道某个原数据的映射值时,只要判断它在第几个元素,反过来就更加简单,直接下标询问。为什么要去重?--我们需要的的是值的大小的映射值,相同的值的映射应该一样,故重复的数据可以去掉,这里的离散化,我大概就这么理解的。

二:线段树,线段树维护的是总区间的覆盖长度嘛,开始我一直有一段代码看不懂:if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l];就是当一个区间被覆盖时,这里的区间又端点要对应加一后的原来的值--因为插入l~r的线段的时候,线段树上更新的是l~r-1区间,但是为什么要这么写,直接更新l~r区间,然后每一个区间的长度就是左右端点的差不行吗?后来看到一句值得话:每个节点应该对应一个区间(不然叶子节点维护的是什么,仔细想一想会发现更新的时候会出现问题的)。所以对于l~r区间的线段,我们用线段树的l~r-1区间去维护(叶子节点的区间长度为1了)。

三:后面的问题就不大了,扫描线只是个虚名罢了,就是把所有的边排一下序,然后顺序扫一遍。

也贴个代码念一些吧:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1010;
struct Edge{
    int lx,rx,h;
    int val;
}edge[maxn<<1];
struct node{
    int l,r;
    int is_cover,len;
    int mid(){return (l+r)>>1;}
}tree[maxn<<3];
bool cmp(Edge e1,Edge e2){
    return e1.h<e2.h;
}
int Pre_x[maxn<<1];
int Now_x[maxn<<1];
void Build_tree(int i,int l,int r){
    tree[i].l=l,tree[i].r=r;
    tree[i].is_cover=0;tree[i].len=0;
    if(l==r)return;
    int m=tree[i].mid();
    Build_tree(i<<1,l,m);
    Build_tree(i<<1|1,m+1,r);
}
void Get_len(int i){
    if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l];
    else if(tree[i].l==tree[i].r)tree[i].len=0;
    else{tree[i].len=tree[i<<1].len+tree[i<<1|1].len;}
}
void Update_tree(int i,int l,int r,int val){
    if(tree[i].l==l&&tree[i].r==r){
        tree[i].is_cover+=val;
        Get_len(i);
        return;
    }
    int m=tree[i].mid();
    if(r<=m)Update_tree(i<<1,l,r,val);
    else if(l>m)Update_tree(i<<1|1,l,r,val);
    else{
        Update_tree(i<<1,l,m,val);
        Update_tree(i<<1|1,m+1,r,val);
    }
    Get_len(i);
}
int main(){
    freopen("in.txt","r",stdin);
    int n,x1,y1,x2,y2;
    while(~scanf("%d",&n)&&n){
        for(int i=0;i<n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            edge[i<<1].lx=edge[i<<1|1].lx=x1;
            edge[i<<1].rx=edge[i<<1|1].rx=x2;
            edge[i<<1].h=y1;edge[i<<1].val=1;
            edge[i<<1|1].h=y2;edge[i<<1|1].val=-1;
            Pre_x[i<<1]=x1;Pre_x[i<<1|1]=x2;
        }
        sort(edge,edge+2*n,cmp);
        sort(Pre_x,Pre_x+2*n);
        int cnt=1;Now_x[0]=Pre_x[0];
        for(int i=1;i<2*n;i++){
            if(Pre_x[i]!=Pre_x[i-1]){
                Now_x[cnt++]=Pre_x[i];
            }
        }
        Build_tree(1,0,cnt-2);
        LL ans=0;
        for(int i=0;i<2*n;i++){
            int l=lower_bound(Now_x,Now_x+cnt,edge[i].lx)-Now_x;
            int r=lower_bound(Now_x,Now_x+cnt,edge[i].rx)-Now_x-1;
            Update_tree(1,l,r,edge[i].val);
            ans+=(LL)(edge[i+1].h-edge[i].h)*tree[1].len;
        }
        printf("%lld\n",ans);
    }
    printf("*\n");
    return 0;
}

题目链接: 点击打开链接

虽然搞了比较久,但是看到AC的那一刻还是很开心的微笑

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值