题意:给n个矩形,每个矩形都有一个矩形的“洞”,矩形和洞的边都与坐标轴平行,求这些带“洞”的矩形覆盖的面积。
数据范围:n<=50000, 0<=x,y<=50000
分析:这题本质还是求矩形面积并,因为一个带“洞”的矩形可以看成是4个矩形。由于矩形数目n和坐标范围均比较大,所以离散化+暴力统计的方法肯定会超时。扫描线的方法我也是第一次学,我的理解是这样的,把所有矩形的2条竖直边(横边也一样)无限延伸就得到扫描线,这些线把所有矩形重新划分为许多不相交的矩形,所有矩形的面积并其实就是夹在这些线之间的矩形的面积之和,夹缝中的矩形的宽就是扫描线之间的距离,关键在于夹缝中矩形的高度是多少?这就要用到线段树了,由于坐标范围和矩形数目是一个数量级的,所以不离散化应该也没关系(我写了离散化)。线段树是根据y轴来建的,保存的关键信息就是当前夹缝中的矩形的高,其实就是区间的覆盖长度,另需保存区间被覆盖的次数以便更新。将从左到有扫描竖线,碰到矩形的左边竖线就覆盖到区间中,碰到右边竖线就将其从区间中删除,这样的话每次查询的区间被覆盖的长度就是夹缝中矩形的高。
注意:
1、建树之前要判断y方向上的坐标数目是否小于2,小于2时若建树会RE。其实小于2说明结果是0
2、最大面积为500000*50000,超了int,可以用unsigned int
#include <stdio.h> #include <string.h> #include <stdlib.h> #define N 200010 int n; struct scan { int x,yl,yu,flag; }; scan scanline[2*N]; int xcnt; int cnt[4*2*N],len[4*2*N]; int y[2*N],ycnt; int cmp1(const void *a,const void *b) { return *(int*)a-*(int*)b; } int cmp2(const void *a,const void *b) { scan *c=(scan *)a; scan *d=(scan *)b; return (c->x)-(d->x); } void init() { xcnt=ycnt=0; } void add(int a,int b,int c,int d) { scanline[xcnt].x=a; scanline[xcnt].yl=b; scanline[xcnt].yu=d; scanline[xcnt++].flag=1; scanline[xcnt].x=c; scanline[xcnt].yl=b; scanline[xcnt].yu=d; scanline[xcnt++].flag=0; y[ycnt++]=b; y[ycnt++]=d; } void read() { int i,j; int x[8]; for(i=0;i<n;i++) { for(j=0;j<8;j++) scanf("%d",&x[j]); if(x[0]<x[4] && x[1]<x[3]) add(x[0],x[1],x[4],x[3]); if(x[6]<x[2] && x[1]<x[3]) add(x[6],x[1],x[2],x[3]); if(x[4]<x[6] && x[7]<x[3]) add(x[4],x[7],x[6],x[3]); if(x[4]<x[6] && x[1]<x[5]) add(x[4],x[1],x[6],x[5]); } } int bs(int k) { int min=0,max=ycnt,mid; while(min+1!=max) { mid=min+max>>1; if(y[mid]>k) max=mid; else min=mid; } return min+1; } void update(int cur,int l,int r) { int ls=cur<<1,rs=cur<<1|1; if(cnt[cur]>0) len[cur]=y[r-1]-y[l-1]; else if(l+1==r) len[cur]=0; else len[cur]=len[ls]+len[rs]; } void build(int cur,int l,int r) { int mid=l+r>>1,ls=cur<<1,rs=cur<<1|1; cnt[cur]=0; len[cur]=0; if(l+1==r) return; build(ls,l,mid); build(rs,mid,r); } void insert(int cur,int l,int r,int s,int t) { int mid=l+r>>1,ls=cur<<1,rs=cur<<1|1; if(l>=s && r<=t) { cnt[cur]++; update(cur,l,r); return; } if(mid>s) insert(ls,l,mid,s,t); if(mid<t) insert(rs,mid,r,s,t); update(cur,l,r); } void del(int cur,int l,int r,int s,int t) { int mid=l+r>>1,ls=cur<<1,rs=cur<<1|1; if(l>=s && r<=t) { cnt[cur]--; update(cur,l,r); return; } if(mid>s) del(ls,l,mid,s,t); if(mid<t) del(rs,mid,r,s,t); update(cur,l,r); } void solve() { qsort(y,ycnt,sizeof(y[0]),cmp1); qsort(scanline,xcnt,sizeof(scanline[0]),cmp2); int i,j,k; k=ycnt; ycnt=0; y[ycnt++]=y[0]; for(i=1;i<k;i++) if(y[ycnt-1]^y[i]) y[ycnt++]=y[i]; if(ycnt<2) { puts("0"); return; } unsigned int ans=0; build(1,1,ycnt); for(k=0;k<xcnt-1;k++) { i=scanline[k].yl; i=bs(i); j=scanline[k].yu; j=bs(j); if(scanline[k].flag) insert(1,1,ycnt,i,j); else del(1,1,ycnt,i,j); i=scanline[k].x; j=scanline[k+1].x; ans+=len[1]*(j-i); } printf("%u\n",ans); } int main() { while(scanf("%d",&n),n) { init(); read(); solve(); } return 0; }