C15【模板】扫描线算法 矩形面积并

视频链接:C15【模板】扫描线算法 矩形面积并 这个是正经板子_哔哩哔哩_bilibili

 

 

 

 

 

 

Luogu P5490 【模板】扫描线

// 扫描线+线段树+离散化 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);
}
View Code

 

POJ1151 Atlantis

// 扫描线+线段树+离散化 
#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;
}

 

posted @ 2023-02-27 15:54  董晓  阅读(574)  评论(0编辑  收藏  举报