HDU 1542 & POJ 1151 Atlantis【线段树扫描线】
来源:互联网 发布:男士泳衣 知乎 编辑:程序博客网 时间:2024/05/18 03:34
扫描线用于求若干个相交矩形的面积并,因为用几何方法在相交的情况复杂的时候难以计算。下面给2个矩形的情况做例子
给定2个矩形对角的点坐标,则下图的面积为图2的三种颜色面积和
图1
图2
现在假设有一条竖着的线从左边往右边扫,扫到矩阵的边时,若是入边,将边这一个区间的cover属性+1,出边则-1
图中圈圈的数字是第几条边,花括号的数是cover值。如图扫描线经过②号边(入边)时最上面部分是1,下面还是1,中间是2,到出边就又减了
图3
则扫描到②号线时,计算一次面积,长为②号线的x减去①号线(前面一条线)的x,高为[Ymin,Ymax]中所有cover为正的高度和
(在这里即为①号线的Yup,Ydown差)。这样重复下去即求得面积和。
放线段树上,区间端点为各条边的上下2个y值,每个区间也都有个cover属性表示覆盖的次数,如上所提。
图4
假设点为[1,1],[2,2],[3,3],[4,4](按图1的情况),则有上图。①号线为[1,3],②为[2,4]...
看这图的第二行,前面一个区间是[1,2],后面是[2,4],而不是{3,4],是因为在矩形里,区间是连续的,[2,3]是存在的,如果为mid+1则[2,3]会被弄没。
第三行:[1,2]没有再继续分成[1,1]和[2,2],是因为这样的点区间没有意义,一条线当然要两个点
然而区间端点并不一定是这么好的整数,即可能是浮点数,还可能坐标很大,所以需要对这些区间的Y值离散化(这里不解释,可见),离散化后就跟上图的1,2,3,4一样了。
NEUQ扫描线-视频链接
HDU 1542 Atlantis 也是POJ 1151 Atlantis 不过POJ用G++交的话记得是%.2f而不是%.2lf
思路一:保存区间时区间端点都用double类型,避免离散化的麻烦。然后只对叶区间处理,如扫描图3里的第②条线时,
面积为②号线上cover为1、为2、为1的三段这和。
这种情况下求每一部分面积时:长为当前线的x减去前面一条线的x,代码里的tree[i].x保存上一条线的x值,可以这样做是因为每次求第i条线时,
上一条线已经扫过,在建树时设置x为-1表示没有上一条。
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <set>#include <map>#include <stack>#include <queue>#include <vector>#include <iostream>#include <iomanip>#include <algorithm>using namespace std;#define ll __int64#define INF 0x7FFFFFFF#define INT_MIN -(1<<31)#define eps 10^(-6)#define Q_CIN ios::sync_with_stdio(false);#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) );#define RE freopen("1.in","r",stdin);#define WE freopen("1.out","w",stdout);#define MOD 10009#define debug(x) cout<<#x<<":"<<(x)<<endl;//#define lson i<<1,l,m//#define rson i<<1|1,m+1,rconst int maxn=105;const int maxN=100010;double y[maxn<<1];struct Line{ double x; double y_up,y_down; int dir; //1为入边,-1为出边}L[maxn<<1]; //2*n的边数struct Tree{ double y_up,y_down,x; //x为上一条线段的x值 int cover; bool leaf; //是否为叶区间}tree[maxN<<2];bool cmp(Line a,Line b){ return a.x<b.x;}void build(int i,int l,int r){ tree[i].x=-1;tree[i].y_up=y[r];tree[i].y_down=y[l];tree[i].cover=0;tree[i].leaf=false; if(l+1==r) //[1,2]这样的区间就不分下去了 { tree[i].leaf=true; return; } int m=(l+r)>>1; build(i<<1,l,m); build(i<<1|1,m,r); //区间是连续的,所以右区间的l要与左区间的r相同}double update(int i,double x,double y_down,double y_up,int dir){ if(y_down>=tree[i].y_up||y_up<=tree[i].y_down) //如在[1,2]里查询[3,4]直接返回0 return 0.0; if(tree[i].leaf) //非叶区间不管 { if(tree[i].cover>0) //有覆盖到,求面积 { double xx=tree[i].x; double ch=(x-xx)*(tree[i].y_up-tree[i].y_down); tree[i].cover+=dir; //边覆盖次数 tree[i].x=x; return ch; }else { tree[i].x=x; //设x为x tree[i].cover+=dir; return 0.0; } } double s1=update(i<<1,x,y_down,y_up,dir); double s2=update(i<<1|1,x,y_down,y_up,dir); return s1+s2;}int main(){ int n; double x1,x2,y1,y2; int te=1;// RE while(cin>>n,n) { int cnt=1; FOR(i,1,n) { cin>>x1>>y1>>x2>>y2; L[cnt].x=x1;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=1;y[cnt]=y1;cnt++; L[cnt].x=x2;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=-1;y[cnt]=y2;cnt++; }// sort(L+1,L+cnt,cmp);// sort(y+1,y+cnt);// build(1,1,cnt-1); sort(L+1,L+2*n+1,cmp); //线段按x排序 sort(y+1,y+2*n+1); //y数组升序 build(1,1,2*n); double ans=0.0; FOR(i,1,2*n) ans+=update(1,L[i].x,L[i].y_down,L[i].y_up,L[i].dir); cout<<"Test case #"<<te++<<endl; cout<<setiosflags(ios::fixed); cout<<"Total explored area: "<<setprecision(2)<<ans<<endl<<endl; } return 0;}
思路二(主流一点):给每个区间增加一个len属性,表示该区间能与下一条线段计算用的高度,然后顺势每次插入线段就用整个大区间(树)与下一条能算的高度求出面积。
(区间存端点有点离散化的思想,把区间存的都变成了常规的整数l,r)如插入②时,因为有y数组排序,插入的区间就是【2,4】,则【2,4】能与下一条计算的是y[4]-y[2],然后通过pushup把【1,2】能计算的也加进来,就可以求②和③中间的面积了。因为区间的l,r是int,所以插入的线段的区间需要用二分在y数组里找出下标。
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <set>#include <map>#include <stack>#include <queue>#include <vector>#include <iostream>#include <iomanip>#include <algorithm>using namespace std;#define ll __int64#define INF 0x7FFFFFFF#define INT_MIN -(1<<31)#define eps 10^(-6)#define Q_CIN ios::sync_with_stdio(false);#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) );#define RE freopen("1.in","r",stdin);#define WE freopen("1.out","w",stdout);#define MOD 10009#define debug(x) cout<<#x<<":"<<(x)<<endl;//#define lson i<<1,l,m//#define rson i<<1|1,m+1,rconst int maxn=105;const int maxN=100010;double y[maxn<<1];int ulen;struct Line{ double x; double y_up,y_down; int dir; //1为入边,-1为出边}L[maxn<<1]; //2*n的边数struct Tree{ int l,r; int cover; double len;}tree[maxN<<2];bool cmp(Line a,Line b){ return a.x<b.x;}void build(int i,int l,int r){ tree[i].l=l; tree[i].r=r; tree[i].cover=0; tree[i].len=0.0; if(l+1==r) return; int m=(l+r)>>1; build(i<<1,l,m); build(i<<1|1,m,r);}int find(double x){ int l=1,r=ulen; while(l<=r) { int mid=(l+r)/2; if(fabs(y[mid]-x)<1e-6) // if(y[mid]==x) return mid; if(y[mid]>x) r=mid-1; else l=mid+1; } return l;}void fun(int i) //包含pushup和完全覆盖的情况下更新{ if(tree[i].cover>0) //有覆盖则整一段可以做为下一条线段计算用 tree[i].len=(y[tree[i].r]-y[tree[i].l]); else if(tree[i].l+1==tree[i].r) //叶子 tree[i].len=0; else tree[i].len=tree[i<<1].len+tree[i<<1|1].len;}void update(int i,int l,int r,int dir){ if(tree[i].l>r||tree[i].r<l) //完全不搭 return; if(l<=tree[i].l&&tree[i].r<=r) //完全包含 { tree[i].cover+=dir; fun(i); return; } update(i<<1,l,r,dir); update(i<<1|1,l,r,dir); fun(i);}int main(){ int n; double x1,x2,y1,y2; int te=1; RE while(cin>>n,n) { int cnt=1; FOR(i,1,n) { cin>>x1>>y1>>x2>>y2; L[cnt].x=x1;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=1;y[cnt]=y1;cnt++; L[cnt].x=x2;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=-1;y[cnt]=y2;cnt++; } //cnt==2*n+1 sort(L+1,L+cnt,cmp); //线段按x排序 sort(y+1,y+cnt); //y数组保存y值,升序,才与线段树区间对应 ulen=1; y[0]=-100005.0; for(int i=1;i<cnt;i++) //y数组去重,默认y[0]与y[1]不同 { if(y[i]!=y[i-1]) { y[ulen++]=y[i]; } } ulen--; // !!!!! build(1,1,ulen); double ans=0.0; FOR(i,1,cnt-1) { //在新线的y区间插入新cover(dir) int a=find(L[i].y_down); //在y数组里找y_down的下标 int b=find(L[i].y_up); update(1,a,b,L[i].dir); //每插入一条就把这条与下一条能算的算起来 ans+=tree[1].len*(L[i+1].x-L[i].x); //tree[1].len为整个树能与i+1条线计算的,因为有pushup操作 } cout<<"Test case #"<<te++<<endl; cout<<setiosflags(ios::fixed); cout<<"Total explored area: "<<setprecision(2)<<ans<<endl<<endl; } return 0;}既然是整型的l,r,那就按常规的线段树飘逸去吧,把tree结构体省了,添加参数代替。
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <set>#include <map>#include <stack>#include <queue>#include <vector>#include <iostream>#include <iomanip>#include <algorithm>using namespace std;#define ll __int64#define INF 0x7FFFFFFF#define INT_MIN -(1<<31)#define eps 10^(-6)#define Q_CIN ios::sync_with_stdio(false);#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) );#define RE freopen("1.in","r",stdin);#define WE freopen("1.out","w",stdout);#define MOD 10009#define debug(x) cout<<#x<<":"<<(x)<<endl;#define lson i<<1,l,m#define rson i<<1|1,m,rconst int maxn=105;const int maxN=100010;double y[maxn<<1];int ulen;struct Line{ double x; double y_up,y_down; int dir; //1为入边,-1为出边 bool operator <(const struct Line &another)const{ return x<another.x; }}L[maxn<<1];int cover[maxN<<2];double len[maxN<<2];void build(int i,int l,int r){ cover[i]=0;len[i]=0.0; if(l+1==r) return; int m=(l+r)>>1; build(lson); build(rson);}void fun(int i,int l,int r) //包含pushup和完全覆盖的情况下更新{ if(cover[i]>0) len[i]=(y[r]-y[l]); else if(l+1==r) len[i]=0; else len[i]=len[i<<1]+len[i<<1|1];}void update(int i,int l,int r,int L,int R,int dir){ if(L<=l&&r<=R) { cover[i]+=dir; fun(i,l,r); return; } int m=(l+r)>>1; if(L<m) //注意这里没有等号,如[1,4]里插入[2,4],不能去左子区间[1,2]找 update(lson,L,R,dir); if(R>m) update(rson,L,R,dir); fun(i,l,r);}int main(){ int n; double x1,x2,y1,y2; int te=1;// RE while(cin>>n,n) { int cnt=1; FOR(i,1,n) { cin>>x1>>y1>>x2>>y2; L[cnt].x=x1;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=1;y[cnt]=y1;cnt++; L[cnt].x=x2;L[cnt].y_down=y1;L[cnt].y_up=y2;L[cnt].dir=-1;y[cnt]=y2;cnt++; } //cnt==2*n+1 sort(L+1,L+cnt); sort(y+1,y+cnt); ulen=1; y[0]=-100005.0; for(int i=1;i<cnt;i++) if(y[i]!=y[i-1]) y[ulen++]=y[i]; ulen--; build(1,1,ulen); double ans=0.0; FOR(i,1,cnt-1) { int a=lower_bound(y+1,y+ulen,L[i].y_down)-y; int b=lower_bound(y+1,y+ulen,L[i].y_up)-y;//因为知道元素一定存在 update(1,1,ulen,a,b,L[i].dir); ans+=len[1]*(L[i+1].x-L[i].x); } printf("Test case #%d\nTotal explored area: %.2lf\n\n",te++,ans); } return 0;}
- HDU 1542 && POJ 1151 Atlantis(线段树+扫描线)
- HDU 1542 & POJ 1151 Atlantis【线段树扫描线】
- poj 1151 hdu 1542 atlantis (线段树+扫描线)
- poj 1151 & hdu 1542 Atlantis(线段树,扫描线)
- poj 1151 Atlantis(线段树 扫描线)
- POJ 1151 - Atlantis 线段树+扫描线..
- poj 1151 Atlantis(线段树+扫描线)
- POJ 1151 - Atlantis 线段树+扫描线..
- POJ 1151 Atlantis 扫描线+线段树
- POJ 1151 Atlantis(扫描线,线段树)
- POJ 1151:Atlantis 线段树+扫描线
- POJ 1151 Atlantis 线段树扫描线
- poj 1151 Atlantis 线段树扫描线
- Atlantis poj 1151 线段树+扫描线
- hdu 1542 Atlantis 线段树扫描线
- HDU 1542 Atlantis(线段树:扫描线)
- 【HDU】1542 Atlantis 线段树+扫描线
- HDU 1542 Atlantis(线段树扫描线)
- (五十二)触摸事件初步
- android 模拟器ko:failed to initialize KVM
- 一步一步理解线段树
- hdu-1160:FatMouse's Speed
- Deep Learning 读书笔记(十三):Improving Word Representations via Global Context and Multiple Word Prototype
- HDU 1542 & POJ 1151 Atlantis【线段树扫描线】
- Codeforces Round #291 Div2
- Windows核心编程001-错误处理
- 设计模式--迭代器模式与组合方法模式
- HDU-武林-模拟
- 【总结】伤寒杂病论Spring经验总结
- JavaBean作用域及测试
- poj 3111 二分求最大化平均值
- SDUT 1569----选夫婿1