C15【模板】扫描线算法 矩形面积并
视频链接:C15【模板】扫描线算法 矩形面积并 这个是正经板子_哔哩哔哩_bilibili
// 扫描线+线段树+离散化 1.6s #include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define ls u<<1 #define rs u<<1|1 const int N=200005; struct line{ //扫描线 int x1,x2,y; int tag; //入边:+1,出边:-1 bool operator<(line &t){return y<t.y;} }L[N]; struct tree{ //线段树 int l,r; int cnt,len; //区间覆盖次数和覆盖长度 }tr[N*8]; int X[N]; //X坐标 void build(int u,int l,int r){ //建树 tr[u]={l,r,0,0}; if(l==r) return; int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); } void pushup(int u){ int l=tr[u].l, r=tr[u].r; //r → X[r+1] if(tr[u].cnt) tr[u].len=X[r+1]-X[l]; else tr[u].len=tr[ls].len+tr[rs].len; } void change(int u,int l,int r,int tag){ //区修 if(l>tr[u].r || r<tr[u].l)return; if(l<=tr[u].l && tr[u].r<=r){ tr[u].cnt+=tag; pushup(u); //这里会爆4倍空间 return; } change(ls,l,r,tag); change(rs,l,r,tag); pushup(u); } int main(){ int n,x1,x2,y1,y2; scanf("%d",&n); for(int i=1; i<=n; i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); L[i]={x1,x2,y1,1}; L[n+i]={x1,x2,y2,-1}; X[i]=x1; X[n+i]=x2; } n*=2; sort(L+1,L+n+1); //扫描线排序 sort(X+1,X+n+1); //X坐标排序 int s=unique(X+1,X+n+1)-X-1; //去重 build(1,1,s-1); long long ans=0; for(int i=1; i<n; i++){ int l=lower_bound(X+1,X+s+1,L[i].x1)-X; int r=lower_bound(X+1,X+s+1,L[i].x2)-X; change(1,l,r-1,L[i].tag); //x2 → r-1 ans+=1ll*tr[1].len*(L[i+1].y-L[i].y); } printf("%lld\n",ans); }
// 扫描线+线段树+离散化 1.4s #include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define ls u<<1 #define rs u<<1|1 const int N=200005; struct line{ //扫描线 int x1,x2,y; int tag; //入边:+1,出边:-1 bool operator<(line &t){return y<t.y;} }L[N]; int cnt[N*8],len[N*8]; //线段树 int X[N]; //X坐标 void pushup(int u,int l, int r){ if(cnt[u]) len[u]=X[r+1]-X[l]; //r → X[r+1] else len[u]=len[ls]+len[rs]; } void change(int u,int l,int r,int a,int b,int tag){ if(a>r || b<l) return; //越界 if(a<=l && r<=b){ //覆盖 cnt[u]+=tag; pushup(u,l,r); return; } int m=l+r>>1; change(ls,l,m,a,b,tag); //裂开 change(rs,m+1,r,a,b,tag); pushup(u,l,r); } int main(){ int n,x1,x2,y1,y2; scanf("%d",&n); for(int i=1; i<=n; i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); L[i]={x1,x2,y1,1}; L[n+i]={x1,x2,y2,-1}; X[i]=x1; X[n+i]=x2; } n*=2; sort(L+1,L+n+1); //扫描线排序 sort(X+1,X+n+1); //X坐标排序 int s=unique(X+1,X+n+1)-X-1; //去重 long long ans=0; for(int i=1; i<n; i++){ int l=lower_bound(X+1,X+s+1,L[i].x1)-X; int r=lower_bound(X+1,X+s+1,L[i].x2)-X; change(1,1,s,l,r-1,L[i].tag); //x2 → r-1 ans+=1ll*(L[i+1].y-L[i].y)*len[1]; } printf("%lld\n",ans); }
// 离散线段树 #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define lc u<<1 #define rc u<<1|1 const int N=200005; struct Line{ //扫描线 int x,y1,y2; int tag; //入边:+1,出边:-1 bool operator<(Line &t){return x<t.x;} }L[N]; struct Tree{ //线段树 int l,r; int len,cnt; //区间覆盖次数和覆盖长度 }tr[N*8]; int n,Y[N]; //Y坐标 void build(int u,int l,int r){ //建树 tr[u]={Y[l],Y[r]}; if(r==l+1) return; //叶子宽度为2 int mid=(l+r)>>1; build(lc,l,mid); build(rc,mid,r); } void pushup(int u){ if(tr[u].cnt) tr[u].len=tr[u].r-tr[u].l; else tr[u].len=tr[lc].len+tr[rc].len; } void change(int u,int l,int r,int tag){ //区修 if(l>=tr[u].r || r<=tr[u].l) return; if(l<=tr[u].l && tr[u].r<=r){ tr[u].cnt+=tag; pushup(u); return; } change(lc,l,r,tag); change(rc,l,r,tag); pushup(u); } int main(){ scanf("%d",&n); int x1,y1,x2,y2; for(int i=1; i<=n; i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); L[i]={x1,y1,y2,1}; L[n+i]={x2,y1,y2,-1}; Y[i]=y1, Y[n+i]=y2; } n*=2; sort(L+1,L+n+1); //扫描线排序 sort(Y+1,Y+n+1); //Y坐标离散化 build(1,1,n); long long ans=0; for(int i=1; i<n; i++){ //枚举扫描线 change(1,L[i].y1,L[i].y2,L[i].tag); ans+=1ll*(L[i+1].x-L[i].x)*tr[1].len; } printf("%lld\n",ans); }
// 扫描线+线段树+离散化 #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define lc u<<1 #define rc u<<1|1 const int N=205; struct line{ //扫描线 double x1,x2,y; int tag; //入边:+1,出边:-1 bool operator<(line &t){return y<t.y;} line(){} line(double x1,double x2,double y,int t):x1(x1),x2(x2),y(y),tag(t){} }L[N]; int cnt[N*8]; double len[N*8]; //线段树 double X[N]; //X坐标 void pushup(int u,int l, int r){ if(cnt[u]) len[u]=X[r+1]-X[l]; //r → X[r+1] else len[u]=len[lc]+len[rc]; } void change(int u,int l,int r,int a,int b,int tag){ if(a>r || b<l) return; //越界 if(a<=l && r<=b){ //覆盖 cnt[u]+=tag; pushup(u,l,r); return; } int m=l+r>>1; change(lc,l,m,a,b,tag); //裂开 change(rc,m+1,r,a,b,tag); pushup(u,l,r); } int main(){ int n,k=0; //矩形个数,样例个数 while(scanf("%d",&n),n){ double x1,x2,y1,y2; for(int i=1; i<=n; i++){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); L[i]=line(x1,x2,y1,1); L[n+i]=line(x1,x2,y2,-1); X[i]=x1; X[n+i]=x2; } n*=2; sort(L+1,L+n+1); //扫描线排序 sort(X+1,X+n+1); //X坐标排序 int s=unique(X+1,X+n+1)-X-1; //去重 memset(cnt,0,sizeof(cnt)); memset(len,0,sizeof(len)); double ans=0; for(int i=1; i<n; i++){ int l=lower_bound(X+1,X+s+1,L[i].x1)-X; int r=lower_bound(X+1,X+s+1,L[i].x2)-X; change(1,1,s,l,r-1,L[i].tag); //x2 → r-1 ans+=len[1]*(L[i+1].y-L[i].y); } printf("Test case #%d\n",++k); printf("Total explored area: %.2f\n\n",ans); } return 0; }