这是求面积并的题目,刚开始我的思路是将挖去的矩形的入边和出边覆盖效果颠倒,
即入边-1,出边+1,后来调试到爆炸,发现这是错误的做法。。原因就是对最简单
的面积并问题没有搞清楚。刚开始接触扫描线的时候我就有一个问题,为什么覆盖
次数不需要向子区间传递,但是我没有仔细去想这个问题,直到遇到这道题目。在
求相交矩形面积并的过程中,所有的线段都是成对出现,重点在于所有线段的覆盖
都是整段整段的操作,所以不需要向下传。而这道题目用这种做法就会出现一个区
间被较小的区间释放的情况,所以会出错。
#include<cstdio> #include<algorithm> using namespace std; const int maxn=310;//最多矩形个数 struct edge{ int x1,x2,y; int f;//1表示入,-1表示出 edge(){} edge(int _x1,int _x2,int _y,int _f){ x1=_x1,x2=_x2,y=_y,f=_f; } bool operator <(edge &e){ if(y!=e.y) return y<e.y; return f>e.f; } }; int nVx; int Vx[maxn*4]; int nSgs; edge Sgs[maxn*4]; int num[maxn*4*4]; int len[maxn*4*4]; void build(int root,int l,int r) { num[root]=len[root]=0; if(l==r)return; int mid=(l+r)/2; build(root*2,l,mid); build(root*2+1,mid+1,r); } void pushUp(int root,int l,int r) { if(num[root]!=0)len[root]=Vx[r+1]-Vx[l]; else if(l==r)len[root]=0; else len[root]=len[root*2]+len[root*2+1]; printf("%d %d num[%d]=%d ",l,r,root,num[root]); } void update(int root,int L,int R,int f,int l,int r) { //printf("%d %d %d %d %d %d ",root,L,R,f,l,r); if(L<=l&&r<=R){ num[root]+=f; pushUp(root,l,r); return ; } int mid=(l+r)/2; if(L<=mid)update(root*2,L,R,f,l,mid); if(mid<R)update(root*2+1,L,R,f,mid+1,r); pushUp(root,l,r); } int bin(int k) { int l=0,r=nVx-1,mid; while(l<=r){ mid=(l+r)/2; if(Vx[mid]==k)return mid; else if(Vx[mid]>k)r=mid-1; else l=mid+1; } return -1; } int myUnique(int a[],int n) { int sz=1; for(int i=1;i<n;i++){ if(a[i]!=a[i-1])a[sz++]=a[i]; } return sz; } int main() { freopen("in.txt","r",stdin); int N; while(scanf("%d",&N)!=EOF&&N!=0){ nVx=0; nSgs=0; for(int i=0;i<N;i++){ int x1,y1,x2,y2,x3,y3,x4,y4; scanf("%d%d%d%d%d%d%d%d", &x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4); printf("%d %d %d %d %d %d %d %d ", x1,y1,x2,y2,x3,y3,x4,y4); Vx[nVx++]=x1,Vx[nVx++]=x2,Vx[nVx++]=x3,Vx[nVx++]=x4; Sgs[nSgs++]=edge(x1,x2,y1,1); Sgs[nSgs++]=edge(x1,x2,y2,-1); Sgs[nSgs++]=edge(x3,x4,y3,-1); Sgs[nSgs++]=edge(x3,x4,y4,1); } sort(Vx,Vx+nVx); nVx=myUnique(Vx,nVx); sort(Sgs,Sgs+nSgs); build(1,0,nVx-1); int area=0; for(int i=0;i<nSgs-1;i++){ int l=bin(Sgs[i].x1); int r=bin(Sgs[i].x2)-1; update(1,l,r,Sgs[i].f,0,nVx-1); printf("%d %d ",len[1],Sgs[i+1].y-Sgs[i].y); area+=len[1]*(Sgs[i+1].y-Sgs[i].y); } printf("%d ",area); } while(1); }
先贴一下思路:海报一张可以切割成4个矩形,然后就是普通的矩形面积并了,利
用线段树维护即可
正确做法
#include<cstdio> #include<algorithm> using namespace std; const int maxn=50310;//最多矩形个数 struct edge{ int x1,x2,y; int f;//1表示入,-1表示出 edge(){} edge(int _x1,int _x2,int _y,int _f){ x1=_x1,x2=_x2,y=_y,f=_f; } bool operator <(edge &e){ if(y!=e.y) return y<e.y; return f>e.f; } }; int nSgs; edge Sgs[maxn*8]; int num[maxn*8]; int len[maxn*8]; void build(int root,int l,int r) { num[root]=len[root]=0; if(l==r)return; int mid=(l+r)/2; build(root*2,l,mid); build(root*2+1,mid+1,r); } void pushUp(int root,int l,int r) { if(num[root]!=0)len[root]=r-l+1; else if(l==r)len[root]=0; else len[root]=len[root*2]+len[root*2+1]; //printf("%d %d num[%d]=%d ",l,r,root,num[root]); } void update(int root,int L,int R,int f,int l,int r) { //printf("%d %d %d %d %d %d ",root,L,R,f,l,r); if(L<=l&&r<=R){ num[root]+=f; pushUp(root,l,r); return ; } int mid=(l+r)/2; if(L<=mid)update(root*2,L,R,f,l,mid); if(mid<R)update(root*2+1,L,R,f,mid+1,r); pushUp(root,l,r); } int main() { //freopen("in.txt","r",stdin); int N; while(scanf("%d",&N)!=EOF&&N!=0){ nSgs=0; int lb=0,rb=maxn; for(int i=0;i<N;i++){ int x1,y1,x2,y2,x3,y3,x4,y4; scanf("%d%d%d%d%d%d%d%d", &x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4); Sgs[nSgs++]=edge(x1,x2,y1,1); Sgs[nSgs++]=edge(x1,x2,y3,-1); Sgs[nSgs++]=edge(x1,x3,y3,1); Sgs[nSgs++]=edge(x1,x3,y4,-1); Sgs[nSgs++]=edge(x4,x2,y3,1); Sgs[nSgs++]=edge(x4,x2,y4,-1); Sgs[nSgs++]=edge(x1,x2,y4,1); Sgs[nSgs++]=edge(x1,x2,y2,-1); lb=min(x1,lb); rb=max(x2,rb); } sort(Sgs,Sgs+nSgs); build(1,lb,rb); long long area=0; for(int i=0;i<nSgs-1;i++){ int l=Sgs[i].x1; int r=Sgs[i].x2-1; if(l<=r) update(1,l,r,Sgs[i].f,lb,rb-1); //printf("%d %d ",len[1],Sgs[i+1].y-Sgs[i].y); area+=(long long)len[1]*(Sgs[i+1].y-Sgs[i].y); } printf("%lld ",area); } //while(1); }
参考资料