1. 程式人生 > >線段樹掃描線總結,求面積,求周長(hdu1542,poj1177)

線段樹掃描線總結,求面積,求周長(hdu1542,poj1177)

這兩天學了掃描線相關內容,特來總結一下:

求面積:

假設是從下往上掃描

(1)離散橫座標

(2)對陣列由高度從小到大排序

(3)對每一條橫線都進行更新,sum[1]表示的是區間橫座標覆蓋的長度,比如說離散化後更新[1,4]區間,實際上呼叫的是update(1,3),這裡是因為我們在push_up的時候,可以求出正確結果sum[rt]=x[r+1]-x[l],這樣就可以求出[1,4]

(4)在對每一條橫線更新後,我們得到了區間的橫座標,那麼乘當向上一條線的高度和當前高度的差,把每次迴圈求出來的面積加起來~

hdu1542 - 求面積

程式碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N=110;
double X[N<<1];
double tree[N<<3];
int add[N<<3];
struct A{
    double l,r,h;
    int f;
}cor[N<<1];

bool cmp(A a,A b){
    return a.h<b.h;
}

void push_up(int l,int r,int rt){
    if(add[rt]){
        tree[rt]=X[r+1]-X[l];
    }
    else if(l==r)tree[rt]=0;
    else tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

void update(int L,int R,int C,int l,int r,int rt){
    if(L<=l&&R>=r){
        add[rt]+=C;
        push_up(l,r,rt);
        return ;
    }
    int m=(l+r)>>1;
    if(L<=m)update(L,R,C,lson);
    if(R>m)update(L,R,C,rson);
    push_up(l,r,rt);
}

int main(){
    int n,cas=1;
    double x1,x2,y1,y2;
    while(scanf("%d",&n)!=EOF){
        if(n==0)break;
        int cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            X[++cnt]=x1;
            cor[cnt].l=x1;
            cor[cnt].r=x2;
            cor[cnt].h=y1;
            cor[cnt].f=1;
            X[++cnt]=x2;
            cor[cnt].l=x1;
            cor[cnt].r=x2;
            cor[cnt].h=y2;
            cor[cnt].f=-1;
        }

        sort(X+1,X+1+cnt);
        sort(cor+1,cor+1+cnt,cmp);
        int nn=unique(X+1,X+1+cnt)-X-1;//表示去重後X陣列的大小[1,nn]
        memset(tree,0,sizeof(tree));
        memset(add,0,sizeof(add));
        double ans=0;
        for(int i=1;i<=cnt;i++){
            int L=lower_bound(X+1,X+1+nn,cor[i].l)-X;
            int R=lower_bound(X+1,X+1+nn,cor[i].r)-X-1;
            update(L,R,cor[i].f,1,nn,1);
            ans+=tree[1]*(cor[i+1].h-cor[i].h);
        }
        printf("Test case #%d\n",cas++);
        printf("Total explored area: %.2f\n\n",ans);
    }
}
/*
3
10 10 20 20
5 15 10 20
10 25 25 30
*/

求周長:

與求面積相似,只是每次加的橫向長度等於sum[1]-last(last記錄上一個sum[1])

seg陣列記錄有幾條豎線,seg*(h[i+1]-h[i])

seg的原理可以這麼理解:

poj1177 - 求周長

程式碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N=5005;
int X[N<<1];
struct A{
    int l,r,h,f;
}cor[N<<1];
int sum[N<<3],add[N<<3],seg[N<<3],lseg[N<<3],rseg[N<<3];
bool cmp(A a,A b){
    return a.h<b.h;
}
void push_up(int l,int r,int rt){
    if(add[rt]){//全覆蓋
        sum[rt]=X[r+1]-X[l];
        seg[rt]=2;
        lseg[rt]=1;
        rseg[rt]=1;
    }
    else if(l==r){//葉子節點且沒被覆蓋
        sum[rt]=seg[rt]=lseg[rt]=rseg[rt]=0;
    }
    else{//非全覆蓋(不覆蓋或半覆蓋)
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
        seg[rt]=seg[rt<<1]+seg[rt<<1|1];
        lseg[rt]=lseg[rt<<1];
        rseg[rt]=rseg[rt<<1|1];
        if(rseg[rt<<1]&&lseg[rt<<1|1])seg[rt]-=2;
    }
}

void update(int L,int R,int C,int l,int r,int rt){
    if(L<=l&&R>=r){
        add[rt]+=C;
        push_up(l,r,rt);
        return ;
    }
    int m=(l+r)>>1;
    if(L<=m)update(L,R,C,lson);
    if(R>m)update(L,R,C,rson);
    push_up(l,r,rt);
}

int main(){
    int n;
    scanf("%d",&n);
    int x1,x2,y1,y2;
    int cnt=0;
    memset(sum,0,sizeof(sum));
    memset(lseg,0,sizeof(lseg));
    memset(rseg,0,sizeof(rseg));
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        X[++cnt]=x1;
        cor[cnt].l=x1;
        cor[cnt].r=x2;
        cor[cnt].h=y1;
        cor[cnt].f=1;
        X[++cnt]=x2;
        cor[cnt].l=x1;
        cor[cnt].r=x2;
        cor[cnt].h=y2;
        cor[cnt].f=-1;
    }
    sort(X+1,X+1+cnt);
    sort(cor+1,cor+1+cnt,cmp);
    int nn=unique(X+1,X+1+cnt)-X-1;
    int last=0,ans=0;
    for(int i=1;i<=cnt;i++){
        int L=lower_bound(X+1,X+1+nn,cor[i].l)-X;
        int R=lower_bound(X+1,X+1+nn,cor[i].r)-X-1;
        update(L,R,cor[i].f,1,nn,1);
        ans+=abs(sum[1]-last);
        ans+=seg[1]*(cor[i+1].h-cor[i].h);
        last=sum[1];
    }
    printf("%d\n",ans);
}