1. 程式人生 > 實用技巧 >線段樹+掃描線

線段樹+掃描線

掃描線求面積

模型

有若干個矩形,給出每個矩形的左下和右上座標,問這些矩形的面積交。

就是陰影部分的面積

演算法流程

用掃描線,我們按每個矩形分成為兩條掃描線,上掃描線(-)和下掃描線(+)。

struct Line {
    double l, r, h;//覆蓋的x軸起點和終點,高度h
    int k;//上/下邊
}a[10005];

那麼面積就是|這條掃描線的高度-上條掃描線的高度|*(掃描線覆蓋的x軸區間大小)
\(ans+=(a[i].h-a[i-1].h)*(掃描線覆蓋的x軸區間大小)\)
掃描線覆蓋的x軸區間大小:用線段樹維護,下邊線上段樹上加,上邊線上段樹上減。
把掃描線排序後,開始處理,h相同先處理+
加入第一條掃描線前:
\(ans+=(a[1].h-a[0].h)*(0)// ans=0\)



加入第二條掃描線前:
掃描線覆蓋的x軸區間大小=2
\(ans+=(a[2].h-a[1].h)*(2),因為a[2].h==a[1].h//ans=0\)

加入第4條掃描線前,掃描線覆蓋的x軸區間大小=4。
\(ans+=(a[1].h-a[0].h)*(4)// ans+=1*4=4\)

實際上這次求的是這部分面積:

加入第4條掃描線前,掃描線覆蓋的x軸區間大小=5。
\(ans+=(a[1].h-a[0].h)*(5)// ans+=1*5=9\)

這次求的面積:

加入第5條掃描線前,掃描線覆蓋的x軸區間大小=4。
\(ans+=(a[1].h-a[0].h)*(4)// ans+=2*4=17\)


這次求的面積:

加入第6條掃描線前,掃描線覆蓋的x軸區間大小=2。

這次求的面積:

所有的面積都不重不漏的計算出來了。

具體實現

把x座標離散化。用lza標記這個區間是否全部覆蓋。

if(lza[i])sum[i]=這個區間的長度
else{
      if(i是葉子節點){
            sum[i]=0
      }      
      else{
            sum[i]=sum[i<<1]+sum[i<<1|1]
      }
}

這樣就可以維護加線段和減線段了

#include  <map>
#include  <set>
#include  <cmath>
#include  <queue>
#include  <cstdio>
#include  <vector>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
#define LL long long
using namespace std;

double z[205];
struct Tree {
    int laz[2000005];
    double sum[2000005];
    void BT(int i, int l, int r) {
        sum[i]=laz[i]=0;
        if(l==r)
            return ;
        int mid=l+r>>1;
        BT(i<<1, l, mid), BT(i<<1|1, mid+1, r);
    }

    void psuh_up(int i, int l, int r) {
        if(laz[i]) { //全部覆蓋
            sum[i]=z[r]-z[l-1];
        } else if(l==r) { //葉子節點
            sum[i]=0;
        } else {
            sum[i]=sum[i<<1]+sum[i<<1|1];
        }

    }
    void up_data(int i, int l, int r, int L, int R, int val) {
        if(R<L)
            return ;
        if(l==L&&r==R) {
            laz[i]+=val;
            psuh_up(i, l, r);
            return ;
        }
        int mid=l+r>>1;
        if(R<=mid)
            up_data(i<<1, l, mid, L, R, val);
        else if(L>mid)
            up_data(i<<1|1, mid+1, r, L, R, val);
        else
            up_data(i<<1, l, mid, L, mid, val), up_data(i<<1|1, mid+1, r, mid+1, R, val);
        psuh_up(i, l, r);
    }
} T;

struct Line {
    double l, r, h;
    int k;//上/下邊
}a[105];

int cmp1(double a, double b){return a<b;}
int cmp2(const Line &a, const Line &b){return a.h==b.h?a.k>b.k:a.h<b.h;}

int main() {

    int n, cas=1;
    double x, y, x2, y2;
    while(scanf("%d", &n), n) {
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf%lf%lf", &x, &y, &x2, &y2);
            a[i*2-1]={x, x2, y, 1};
            a[i*2]={x, x2, y2, -1};
            z[i*2-1]=x, z[i*2]=x2;
        }

        //離散化x座標
        sort(z+1, z+2*n+1, cmp1);
        int cnt=unique(z+1, z+2*n+1)-z-1;
        T.BT(1, 1, cnt);
        for(int i=1; i<=2*n; i++){
            a[i].l=lower_bound(z+1, z+cnt+1, a[i].l)-z;
            a[i].r=lower_bound(z+1, z+cnt+1, a[i].r)-z;
        }

        double ans=0;
        sort(a+1, a+2*n+1, cmp2);
        for(int i=1; i<=2*n; i++){
            double H=a[i].h-a[i-1].h;
            ans+=H*T.sum[1];
            T.up_data(1, 1, cnt, a[i].l+1, a[i].r, a[i].k);
        }
        printf("Test case #%d\n", cas++);
        printf("Total explored area: %.2f\n\n", ans);
    }

    return 0;
}
/*
2
10 10 20 20
15 15 25 25.5
0
*/