線段樹小引申(尋找最近的沒被標記過的點)

我們經常會用到這樣的代碼

if(mark[i])i++;//i--

很明顯,這個代碼是用來在序列上尋找距離當前節點最近的沒有被標記的節點
但這個代碼複雜度爲O(n) ,有些時候並不見得能過時間複雜度
於是我們可以運用線段樹的原理設計一個O(logn2) 複雜度的算法來解決這個問題

我們用線段樹的每個節點代表一個區間
在樹上進行整個標記和查找的操作
在這棵樹上操作時,
需要尋找的是在當前節點A 之前的第一個沒被標記過的節點B
很顯然,如果當前區間全部被標記,需要繼續up
並且,我們肯定是從LCA(A,B) 的右子樹up 上來
從它的左子樹down 下去的
於是,如果我們是從左子樹up 上來的,需要繼續up
同樣的,如果當前節點的左子樹已被標記,也還需要繼續up
至於down 的操作就很無腦了
而找到了這個點之後就可以在樹上對其進行標記操作

代碼如下:

int tree[M<<2],A[M],B[M<<2];
//A代表序列中的點在樹上的編號
//B代表樹上的點在序列中的編號
//tree代表序列上某一區間是否全部被標記
void build(int l,int r,int p){
    if(l==r){
        A[l]=p;
        B[p]=l;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,(p<<1));
    build(mid+1,r,(p<<1)|1);
}
int query(int p){
    if(tree[p]){
        int pre=p;p>>=1;
        while(tree[p<<1]||pre==(p<<1)){
//①區間所有點都被標記過(包含於第②種情況,可以忽略)②該區間的左子樹中的點都被標記過③先前是從左子樹中up上來的
//這三種情況則需要繼續up
            pre=p;
            p>>=1;
        }
        p<<=1;
        while(p&&!B[p]){
            if(tree[(p<<1)|1])p<<=1;
            else p=(p<<1)|1;
        }
    }
    return p;
}
void update(int p){
    do tree[p]=1,p>>=1;
    while(!tree[p]&&tree[p<<1]&&tree[(p<<1)|1]);
}
發佈了58 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章