1. 程式人生 > >線段樹掃描線總結:矩形面積並&面積交&周長交

線段樹掃描線總結:矩形面積並&面積交&周長交

(有任何問題歡迎留言或私聊 && 歡迎交流討論哦

目錄

面積並

  • 把每個矩形分成上下兩條邊,記錄左右端點和高度,從下向上掃描,線段樹維護x軸上的有效長度。
  • 每次累加部分面積:Δh×lenΔh=highhigh,len
  • 矩形下邊標記為1,下邊標記為-1.
  • 線段樹每個葉子節點表示的是區間[rt,rt+1)的線段長度。(
    rt
    表示的是離散化後的標號)
  • 這樣每次update(l,r1)push_up的時候,len=all[r+1]all[l],因為你更新的時候右下標減了1,所以計算的時候要把1補回來。
  • 為什麼每個節點表示的是[rt,rt+1)這段長度呢?這樣update(l,r1)呢?
  • 這樣做是為了避免漏掉一段線段的情況,類似於POJ 2528這題的離散化要加一的操作。類似但不完全一樣。

AC程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm> #include<assert.h> #include<bitset> #define lson rt<<1 #define rson rt<<1|1 #define lsonl l,mid,rt<<1 #define rsonr mid+1,r,rt<<1|1 #define lowbit(x) (x)&(-(x)) #define all(x) (x).begin(),(x).end() using namespace std; typedef long long
LL; const int INF = 0x3f3f3f3f; const int N = (int)1e5 +107; struct lp{ double l,r,h; int id; }seg[N]; bool cmp(const lp &a, const lp &b){ return a.h<b.h; } int n, m, tot; int cnt[N<<2]; double sum[N<<2], all[N]; void push_up(int l,int r,int rt){ if(cnt[rt]) sum[rt] = all[r+1]-all[l]; else if(l==r) sum[rt] = 0; else sum[rt]=sum[lson]+sum[rson]; } void update(int L,int R,int c,int l,int r,int rt){ if(L<=l&&r<=R){ cnt[rt] += c; push_up(l,r,rt); return; } int mid = (l+r)>>1; if(L>mid) update(L,R,c,rsonr); else if(R<=mid) update(L,R,c,lsonl); else { update(L,R,c,lsonl);update(L,R,c,rsonr); } push_up(l,r,rt); } int main(){ int tc=0; while(~scanf("%d",&n)&&n){ for(int i=0;i<n;++i){ double x1,x2,y1,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); seg[i].l=x1;seg[i].r=x2;seg[i].h=y1;seg[i].id=1; seg[i+n].l=x1;seg[i+n].r=x2;seg[i+n].h=y2;seg[i+n].id=-1; all[i]=x1,all[i+n]=x2; } m = 2*n; sort(seg,seg+m,cmp); sort(all,all+m); int k=0; for(int i=1;i<m;++i){ if(all[i]!=all[i-1])all[++k]=all[i]; } memset(cnt,0,sizeof(cnt)); memset(sum,0,sizeof(sum)); double ans = 0; for(int i=0;i<n*2-1;++i){ int l = lower_bound(all,all+k+1,seg[i].l)-all; int r = lower_bound(all,all+k+1,seg[i].r)-all; if(l<r) update(l,r-1,seg[i].id,0,k,1); ans += sum[1]*(seg[i+1].h-seg[i].h); } printf("Test case #%d\n", ++tc); printf("Total explored area: %.2f\n\n", ans); } return 0; }

面積交

  • 把每個矩形分成上下兩條邊,記錄左右端點和高度,從下向上掃描,線段樹維護x軸上的有效長度。
  • 每次累加部分面積:Δh×twoΔh=highhigh,two
  • 矩形下邊標記為1,下邊標記為-1.
  • 線段樹每個葉子節點表示的是區間[rt,rt+1)的線段長度。(rt表示的是離散化後的標號)
  • two維護的是出現兩次的區間的長度,one維護的和上一題的len一樣,cnt[rt]維護改段出現次數。
  • 這裡需要修改一下push_up函式。細節看程式碼。

AC程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#include<bitset>
#define lson rt<<1
#define rson rt<<1|1
#define lsonl l,mid,rt<<1
#define rsonr mid+1,r,rt<<1|1
#define lowbit(x) (x)&(-(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = (int)1e5 +107;
struct lp{
  double l,r,h;
  int id;
}seg[N];
bool cmp(const lp &a, const lp &b){
  return a.h<b.h;
}
int n, m, tot, cnt[N<<2];
double one[N<<2], two[N<<2];
double all[N];
void push_up(int l,int r,int rt){
  if(cnt[rt]>=2){
    one[rt] = two[rt] = all[r+1]-all[l];
  }else if(cnt[rt]==1){
    one[rt] = all[r+1]-all[l];
    if(l==r)two[rt] = 0;
    else two[rt]=one[lson]+one[rson];
  }else{
    if(l==r)two[rt]=one[rt]=0;
    else{
      one[rt]=one[lson]+one[rson];
      two[rt]=two[lson]+two[rson];
    } 
  }
}
void update(int L,int R,int c,int l,int r,int rt){
  if(L<=l&&r<=R){
    cnt[rt] += c;
    push_up(l,r,rt);
    return;
  }
  int mid = (l+r)>>1;
  if(L<=mid)update(L,R,c,lsonl);
  if(R>mid)update(L,R,c,rsonr);
  push_up(l,r,rt);
}
int main(){
  int tim;
  scanf("%d",&tim);
  while(tim--){
    scanf("%d",&n);
    for(int i=0;i<n;++i){
      double x1,x2,y1,y2;
      scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
      seg[i].l=x1;seg[i].r=x2;seg[i].h=y1;seg[i].id=1;
      seg[i+n].l=x1;seg[i+n].r=x2;seg[i+n].h=y2;seg[i+n].id=-1;
      all[i]=x1,all[i+n]=x2;
    }
    m = 2*n;
    sort(seg,seg+m,cmp);
    sort(all,all+m);
    int k = unique(all, all+m)-all-1;
    memset(cnt,0,sizeof(cnt));
    memset(one,0,sizeof(one));
    memset(two,0,sizeof(two));
    double ans = 0;
    for(int i=0;i<n*2-1;++i){
      int l = lower_bound(all,all+k+1,seg[i].l)-all;
      int r = lower_bound(all,all+k+1,seg[i].r)-all;