1. 程式人生 > >zkw線段樹解決區間rmq

zkw線段樹解決區間rmq

zkw線段樹具體內容請百度統計的力量(這是他講的時候所用的ppt的名字)

今天我們就來完整的寫一個zkw線段樹。

正如他在ppt裡講的

*差分是化絕對為相對的重要手段 *標記永久化後就是值,只不過這種值是相對的 *計算答案時可以利用從節點到根部的資訊 在zkw樹中,每個節點存的都不是最大值M[x], 而是M[x] - M[x<<1](這裡M[x<<1]就是他父親存在最大值)。這樣有什麼用呢?你可以試一下從某個結點開始,走到根,你會發現一路上的和加起來,就是這裡存在最大值。這樣,單點Query就解決了。單點更新也比較簡單,就是一點一點從葉子結點處開始修改,維護這棵樹作為差分樹的性質。只這樣看的話,並不比普通的線段樹有多大優勢。
所以接下來我們看區間更新和區間查詢。 區間更新時,由於是整個區間都變化,這就造成什麼了呢,如果有兩個葉子節點都在要更新的區間裡,他倆父親相同,而且他倆也非端點,這時,由於他倆是一起更新的,你會發現這時他倆的相對差值仍為變化,而且,由於他們自身存的是差分值,而且他們的最大值也同等增加了相同值,所以這倆個節點存的值並不需要改變,需要改變的是哪裡呢?就是邊界節點和跟邊界節點通父親的點,這樣底層的更新最多4個點。解決完底層,它的上一層也是同理,從而每層都最多隻要四個點。當左右邊界同父親的時候,在往上都只要更新兩個點。這時在和線段樹比較的話,你會發現,更新的點的個數少了很多有木有,當更新的層數越來越深的時候,zkw樹至多4k個點,而普通線段樹則是2^k個點左右。這時赤裸裸的差距啊有木有。

至於區間查詢,由於每個點存的內容都和它的兒子沒有關係,所以某種意義上我們可以直接找到區間的邊界點,然後開始一層一層向上累加去個最大or最小就可以了。但是這個找邊界點的過程還會浪費點地方。不如直接就從邊界的葉子節點處一層一層向上累加,每到一層,都注意去最大or最小,來維護答案。

然後就是他的ppt中的錯誤,第一處在add函式裡,它沒有上浮到根節點,這樣的話單就下次查詢來說,並不會造成什麼(你可以試試,這是從某點一直到根的路徑中的sum仍為這個點存的Max),但是,如果有了另一次更新,這時,就會出現問題(出個資料模擬一下就可以)。所以在add函式的尾部還要加一部上浮到根節點的語句。

第二處在Query函式中,如果這時查詢的不是區間是點,這時會有l=r,進一次迴圈,你發現l=r=0,這時,很明顯,函式最後的那步上浮到根節點的迴圈是出不去了。

第三處仍在Query函式中,那就是這時的查詢不再是開區間了,而是閉區間。比如區間的左節點是某個點的右兒子,那麼這時的開區間頭便是某個點的左兒子。這時按照zkw的慣例,下次的開區間節點就是他的父親了,但是由於他的父親的兒子中有區間內的點,從而這個父親也算區間內的點,那麼這時會出現了矛盾,也就是說,本來要經歷的點卻沒有經歷。這樣肯定就有問題了,所以我們只能閉區間查詢,這樣才不會發生上浮一層之後,點與區間的位置關係發生改變。

好了,這次的總結就差不多了。這裡是cf上的一個題目,考的就是這個。底下附上我的程式碼,可以看一下細節部分。

#include <bits/stdc++.h>

#define up(i, lower, upper) for(int i = lower; i < upper; i++)
#define down(i, lower, upper) for(int i = upper-1; i >= lower; i--)

using namespace std;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef vector<int> vi;
typedef vector<pii> vpii;
typedef __int64 ll;
typedef unsigned __int64 ull;

const double pi = acos(-1);
const double eps = 1.0e-9;

template<class T>
inline char read(T &n){
    T x = 0, tmp = 1; char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return c;
}

template <class T>
inline void write(T n) {
    if(n < 0) {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n) {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}

//---------------------------------------------------------
struct SegTree {
    ll tree[550000];
    int m;

    void build(int len) {
        m = 1;
        memset(tree, 0, sizeof tree);
        while(m-1 <= len) m*=2;
        up(i, 1, len+1) read(tree[i+m]);
        down(i, 1, m) tree[i] = min(tree[(i<<1)], tree[(i<<1)+1]);
        down(i, 1, len+m+1) tree[i] = tree[i] - tree[i>>1];
    }

    void update(int l, int r, int val) {
        ll tmp;
        for(l += m-1, r += m+1; l^r^1; l>>=1, r>>=1) {
            if(~l&1) tree[l^1]+=val;
            if(r&1) tree[r^1]+=val;
            tmp = min(tree[l], tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp;
            tmp = min(tree[r], tree[r^1]), tree[r] -= tmp, tree[r^1] -= tmp, tree[r>>1] += tmp;
        }
        for (;l!=1;l>>=1)
            tmp = min(tree[l],tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp;
    }

    ll query(int l, int r) {
        ll lAns = 0, rAns = 0;
        l+=m, r+=m;
        if(l != r) {
            for(; l^r^1; l>>=1, r>>=1) {
                lAns += tree[l], rAns += tree[r];
                if(~l&1) lAns = min(lAns, tree[l^1]);
                if(r&1) rAns = min(rAns, tree[r^1]);
            }
        }
        ll ans = min(lAns+tree[l], rAns+tree[r]);
        while(l > 1) ans += tree[l>>=1];
        return ans;
    }
};

SegTree a;

int main() {
    int len, n, m, l, r;
    read(len);
    a.build(len);
    read(n);
    while(n--) {
        read(l);
        if(read(r) != '\n') {
            read(m);
            if(l > r) a.update(l+1, len, m), a.update(1, r+1, m);
            else a.update(l+1, r+1, m);
        }
        else {
            if(l > r) write(min(a.query(l+1, len), a.query(1, r+1)));
            else write(a.query(l+1, r+1));
            puts("");
        }
    }
    return 0;
}