poj1151 Atlantis——扫描线+线段树
题目:http://poj.org/problem?id=1151
经典的扫描线问题;
可以用线段树的每个点代表横向被矩形上下边分割开的每一格,这样将一个矩形的出现或消失化为线段树上的单点修改;
每个格子记录两个值:c(矩形存在情况),sum(对当前答案作出贡献的长度);
将y离散化作为建树的依据;
一开始没想到线段树上的点应该是横向的格子,写了个乱七八糟:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int t,n,xt; double s[805],ans,tr[805],mx,eps=0.36; struct P{ double x,y1,y2; int tp; P(int x=0,int y1=0,int y2=0,int t=0):x(x),y1(y1),y2(y2),tp(t) {} }p[805]; void pushup(int x) { tr[x]=tr[x<<1]+tr[x<<1|1]; } void add(int x,double l,double r,double L,double R,int w) { // cout<<x<<endl; // printf("l=%lf r=%lf eps=%lf\n",l,r,eps); if(r-l<eps) { s[x]+=w; if(s[x])tr[x]=1; else tr[x]=0; // printf("s[%d]=%d\n",x,s[x]); return; } double mid=(l+r)/2; if(mid>=L)add(x<<1,l,mid,L,R,w); if(mid<R)add(x<<1|1,mid+1,r,L,R,w); pushup(x); } bool cmp(P x,P y){return x.x<y.x;} int main() { while(scanf("%d",&n)==1) { t++;xt=0;mx=0;ans=0; memset(tr,0,sizeof tr); memset(s,0,sizeof s); if(!n)return 0; for(int i=1;i<=n;i++) { xt++;scanf("%lf%lf",&p[xt].x,&p[xt].y1); xt++;scanf("%lf%lf",&p[xt].x,&p[xt].y1); p[xt-1].y2=p[xt].y1;p[xt].y2=p[xt-1].y1; p[xt-1].tp=1;p[xt].tp=-1; mx=max(mx,max(p[xt].y1,p[xt].y2)); } sort(p+1,p+xt+1,cmp); double lst=-1; for(int i=1;i<=xt;i++) { if(p[i].y1>p[i].y2)swap(p[i].y1,p[i].y2); add(1,0,mx,p[i].y1,p[i].y2,p[i].tp); if(lst!=-1)ans+=(p[i].x-lst)*tr[1]; lst=p[i].x; } printf("Test case #%d\nTotal explored area: %.2lf \n",t,ans); } return 0; }
然后参考别人的博客,努力理解了半天,模仿着打了出来,终于A了...
学到了add()函数里面那种二分的方法。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,ct,t; double ans,y[205]; struct P{ int l,r,c;//c为上下矩形抵消数 double ly,ry,sum;//sum为此时计入计算的长度 }tr[805]; struct E{ int tp; double x,ly,ry; }ed[205]; bool cmp(E x,E y){return x.x<y.x;} void build(int x,int l,int r) { tr[x].l=l;tr[x].r=r; tr[x].ly=y[l];tr[x].ry=y[r]; tr[x].c=0;tr[x].sum=0; if(l==r-1)return; int mid=(l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid,r);//不是mid+1,因为l,r表示此块上下两边 } void pushup(int x) { if(tr[x].c>0)//全选 tr[x].sum=tr[x].ry-tr[x].ly; else if(tr[x].l==tr[x].r-1)//仅有一格且被退出 tr[x].sum=0; else//可能有其他边覆盖 tr[x].sum=tr[x<<1].sum+tr[x<<1|1].sum; } void add(int x,E e) { if(tr[x].ly==e.ly&&tr[x].ry==e.ry) { tr[x].c+=e.tp; pushup(x); return; } if(tr[x<<1].ry>=e.ry)add(x<<1,e); else if(tr[x<<1|1].ly<=e.ly)add(x<<1|1,e); else { E tmp=e; tmp.ry=tr[x<<1].ry; add(x<<1,tmp); tmp=e; tmp.ly=tr[x<<1|1].ly; add(x<<1|1,tmp); } pushup(x); } int main() { while(scanf("%d",&n)==1) { t++; if(!n)return 0; ct=0;ans=0; for(int i=1;i<=n;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); y[++ct]=y1;ed[ct].tp=1; ed[ct].x=x1;ed[ct].ly=y1;ed[ct].ry=y2; y[++ct]=y2;ed[ct].tp=-1; ed[ct].x=x2;ed[ct].ly=y1;ed[ct].ry=y2; } sort(ed+1,ed+ct+1,cmp); sort(y+1,y+ct+1); build(1,1,ct); add(1,ed[1]); for(int i=2;i<=ct;i++) { ans+=tr[1].sum*(ed[i].x-ed[i-1].x); add(1,ed[i]);//上下勿反 } printf("Test case #%d\nTotal explored area: %.2f\n\n",t,ans); } return 0; }