POJ 1151 扫描线 线段树
Posted pkgunboat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 1151 扫描线 线段树相关的知识,希望对你有一定的参考价值。
题意:给定平面直角坐标系中的N个矩形,求它们的面积并。
题解:建立一个四元组(x,y1,y2,k).(假设y1<y2)用来储存每一条线,将每一条线按x坐标排序。记录所有的y坐标以后排序离散化。离散化之后线段树的第i个叶子节点储存的是y[i+1]-y[i].
这里的线段树用的是一个不用下传延迟标记的做法(仅限这一类题)。线段树的每一个节点维护length(这个节点的子节点覆盖的长度)和cnt(这个节点代表的线段[l,r]所覆盖的次数)。
任意一个区间都可以被线段树划分成O(logn)个子区间,我们把这些节点的cnt值加上1或减去1。
在修改任意一个节点的cnt时或者向上维护信息的时候,如果这个节点的cnt>0,则这个节点所代表的区间覆盖的长度是y[r+1]-y[l],否则是子节点的长度和。
代码:
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<map> using namespace std; map<double,int> mp; const int maxn=200010; struct ST{ int l,r,cnt; double length; }t[4*maxn]; struct node{ double x,y1,y2; int k; bool operator <(const node& rhs)const{ return x<rhs.x; } }a[maxn]; double b[maxn]; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; t[p].cnt=0; t[p].length=0; if(l==r){ return; } int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); } void maintain(int p){ if(t[p].l==t[p].r)t[p].length=0; else t[p].length=t[p*2].length+t[p*2+1].length; } void change(int p,int l,int r,int k){ if(l<=t[p].l&&r>=t[p].r){ t[p].cnt+=k; if(t[p].cnt>0){ t[p].length=b[t[p].r+1]-b[t[p].l]; } else maintain(p); return; } int mid=(t[p].l+t[p].r)/2; if(mid>=l)change(p*2,l,r,k); if(mid<r)change(p*2+1,l,r,k); if(t[p].cnt>0)t[p].length=b[t[p].r+1]-b[t[p].l]; else maintain(p); } int main(){ int n,kase=0; double x1,x2,y1,y2; double ans=0; while(~scanf("%d",&n)&&n){ ans=0; mp.clear(); for(int i=1;i<=n;i++){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); a[i]=(node){x1,y1,y2,1}; a[i+n]=(node){x2,y1,y2,-1}; b[i]=y1,b[i+n]=y2; } sort(a+1,a+1+2*n); sort(b+1,b+1+2*n); int m=unique(b+1,b+1+2*n)-(b+1); for(int i=1;i<=m;i++) mp[b[i]]=i; build(1,1,m-1); for(int i=1;i<2*n;i++){ int l=mp[a[i].y1],r=mp[a[i].y2]-1; change(1,l,r,a[i].k); ans+=(a[i+1].x-a[i].x)*(t[1].length); } printf("Test case #%d Total explored area: %.2f ",++kase,ans); } return 0; }
以上是关于POJ 1151 扫描线 线段树的主要内容,如果未能解决你的问题,请参考以下文章
POJ1151Atlantis 矩形面积并[线段树 离散化 扫描线]