1. 程式人生 > >離散化-線段樹-掃描線小結

離散化-線段樹-掃描線小結

昨天打了南寧賽區的網路賽,碰到一個求矩形並的面積的模板題,然而沒A出來(不會啊哭)。以前也碰到過類似線段樹+掃描線的題目,只能怪自己以前沒補題吧哎~。

關於這個知識點,網上隨便一搜就能搜到大堆,就說一下我在學習的過程中碰到的問題。

一:首先是離散化,定義就不說了(其實不知道2333),我籠統地理解就是把大的資料範圍縮小,同時又保證資料之間地關係不變,比如說座標的大小,如果給你一些點,你需要的屬性只是點與點之間的距離(與各個點的具體座標值無關),那麼我們可以把點的座標對映到一個比較小的範圍,然後把原來的屬性放到新的座標上就好,一個最簡單的例子:對於點(-1e8,0),(1e8,0)之間的距離,如果要求的是兩個點之間的水平距離,那麼我們把-1e8對映到1,1e8對映到2,那麼距離就是1到2的距離。這只是一個很籠統的說法,總之我理解的就是把很多點壓縮到一起,同時記錄點與點之間的屬性,當你詢問點與點之間的關係時,就可以直接通過查詢新的點得到。

迴歸主題,這裡的離散化的作用也是化大座標為小座標(我這裡自下而上掃),方便建樹,比如原來最大範圍-10000~10000的線段,而兩個值對應的對映為1~100,那麼我們就只要建立[1~100]的線段樹,對於每一個區間的實際長度,再根據左右兩個端點對應的值計算。那麼如何保證能通過對映能相互查詢到呢--把原資料排序並去重,然後記每一個數的對映值就是下標,那麼我們當要知道某個原資料的對映值時,只要判斷它在第幾個元素,反過來就更加簡單,直接下標詢問。為什麼要去重?--我們需要的的是值的大小的對映值,相同的值的對映應該一樣,故重複的資料可以去掉,這裡的離散化,我大概就這麼理解的。

二:線段樹,線段樹維護的是總區間的覆蓋長度嘛,開始我一直有一段程式碼看不懂:if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l];就是當一個區間被覆蓋時,這裡的區間又端點要對應加一後的原來的值--因為插入l~r的線段的時候,線段樹上更新的是l~r-1區間,但是為什麼要這麼寫,直接更新l~r區間,然後每一個區間的長度就是左右端點的差不行嗎?後來看到一句值得話:每個節點應該對應一個區間(不然葉子節點維護的是什麼,仔細想一想會發現更新的時候會出現問題的)。所以對於l~r區間的線段,我們用線段樹的l~r-1區間去維護(葉子節點的區間長度為1了)。

三:後面的問題就不大了,掃描線只是個虛名罷了,就是把所有的邊排一下序,然後順序掃一遍。

也貼個程式碼念一些吧:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1010;
struct Edge{
    int lx,rx,h;
    int val;
}edge[maxn<<1];
struct node{
    int l,r;
    int is_cover,len;
    int mid(){return (l+r)>>1;}
}tree[maxn<<3];
bool cmp(Edge e1,Edge e2){
    return e1.h<e2.h;
}
int Pre_x[maxn<<1];
int Now_x[maxn<<1];
void Build_tree(int i,int l,int r){
    tree[i].l=l,tree[i].r=r;
    tree[i].is_cover=0;tree[i].len=0;
    if(l==r)return;
    int m=tree[i].mid();
    Build_tree(i<<1,l,m);
    Build_tree(i<<1|1,m+1,r);
}
void Get_len(int i){
    if(tree[i].is_cover)tree[i].len=Now_x[tree[i].r+1]-Now_x[tree[i].l];
    else if(tree[i].l==tree[i].r)tree[i].len=0;
    else{tree[i].len=tree[i<<1].len+tree[i<<1|1].len;}
}
void Update_tree(int i,int l,int r,int val){
    if(tree[i].l==l&&tree[i].r==r){
        tree[i].is_cover+=val;
        Get_len(i);
        return;
    }
    int m=tree[i].mid();
    if(r<=m)Update_tree(i<<1,l,r,val);
    else if(l>m)Update_tree(i<<1|1,l,r,val);
    else{
        Update_tree(i<<1,l,m,val);
        Update_tree(i<<1|1,m+1,r,val);
    }
    Get_len(i);
}
int main(){
    freopen("in.txt","r",stdin);
    int n,x1,y1,x2,y2;
    while(~scanf("%d",&n)&&n){
        for(int i=0;i<n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            edge[i<<1].lx=edge[i<<1|1].lx=x1;
            edge[i<<1].rx=edge[i<<1|1].rx=x2;
            edge[i<<1].h=y1;edge[i<<1].val=1;
            edge[i<<1|1].h=y2;edge[i<<1|1].val=-1;
            Pre_x[i<<1]=x1;Pre_x[i<<1|1]=x2;
        }
        sort(edge,edge+2*n,cmp);
        sort(Pre_x,Pre_x+2*n);
        int cnt=1;Now_x[0]=Pre_x[0];
        for(int i=1;i<2*n;i++){
            if(Pre_x[i]!=Pre_x[i-1]){
                Now_x[cnt++]=Pre_x[i];
            }
        }
        Build_tree(1,0,cnt-2);
        LL ans=0;
        for(int i=0;i<2*n;i++){
            int l=lower_bound(Now_x,Now_x+cnt,edge[i].lx)-Now_x;
            int r=lower_bound(Now_x,Now_x+cnt,edge[i].rx)-Now_x-1;
            Update_tree(1,l,r,edge[i].val);
            ans+=(LL)(edge[i+1].h-edge[i].h)*tree[1].len;
        }
        printf("%lld\n",ans);
    }
    printf("*\n");
    return 0;
}

題目連結:點選開啟連結

雖然搞了比較久,但是看到AC的那一刻還是很開心的微笑