昨天打了南宁赛区的网络赛,碰到一个求矩形并的面积的模板题,然而没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的那一刻还是很开心的。