1. 程式人生 > 實用技巧 >Luogu P3372 【模板】線段樹 1

Luogu P3372 【模板】線段樹 1

思路

線段樹1是一道線段樹的經典模板題,所涉及的線段樹基礎知識也比較全面,作為線段樹初學者(比如我)的練手題就非常合適。這道題想讓我們完成的是對一個序列的區間修改和區間查詢。關於這兩個操作,

我們要引入一個新的東西——lazytag。

關於線段樹的一些基礎寫法在這裡不再多贅述,我主要來講一下有關lazytag的用法和與之相關的push_down函式。

lazytag主要是應用於區間修改的這個操作,主要就是為了在時間複雜度上進一步優化。當我們每次要修改某個區間的值,我們就可以在這個區間的節點上打一個標記,代表這個位置需要進行的修改,而不

用遍歷到每一個葉子結點,節約了時間。但是,在進行更新和求值的時候,一定不能忘了先push_down,要不然會炸得很慘。

如果這樣說還是難以理解,我們可以舉個例子:

我們建立如上的一顆線段樹,假設我們要對2~6這個區間進行修改(一定注意更新之前先把之前的標記下傳並清空)。我們從根節點開始遍歷,發現根節點的左兒子包括2~6這個區間的一部分,所以我們

就向根節點的左兒子遍歷;接下來我們發現1~2節點和2~4節點都包括2~6節點的部分,所以我們就分別向兩邊遍歷;接下來我們發現3~4這個節點所維護的區間完全在我們想要更新的區間裡,那麼我們就

沒有必要再向下遍歷,直接更新3~4這個節點的tag並返回即可。對於其他點的處理也是一樣的,在這裡就不一一列舉了。最後強調的一點就是,在我們想要進行下一次更新或者求區間和的操作時,一定

要把上一次的標記清空。這個一定要記住,否則你做題DeBug的時候會遭受非常大的折磨。

別看在我上面舉的那個例子好像和直接暴力更新區別不大,但是當你的資料量大起來的時候,這個操作就顯得尤為重要。再說一遍,在更新和求值之前千萬不要忘了標記下放啊!!!

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
typedef long long ll;
ll n,m,a[MAXN];
ll tree[MAXN << 2], tag[MAXN << 2];
inline int lson(ll x) {return x << 1;}
inline int rson(ll x) {return x << 1 | 1;}
inline void push_up(ll k){
    tree[k] = tree[lson(k)] + tree[rson(k)];
    return;
}
inline void push_down(ll k,ll l,ll r){
    ll mid = (l + r) >> 1;
    tag[lson(k)] += tag[k];
    tree[lson(k)] += (mid - l + 1) * tag[k];
    tag[rson(k)] += tag[k];
    tree[rson(k)] += (r - mid) * tag[k];
    tag[k] = 0;
    return;
}
void build(ll k,ll l,ll r){
    if(l==r){
        tree[k]=a[l];
        return;
    }
    ll mid = (l + r) >> 1;
    build(lson(k), l, mid);
    build(rson(k), mid + 1, r);
    push_up(k);
    return;
}
void update(ll k,ll l,ll r,ll cl,ll cr,ll v){
    if(cl<=l&&r<=cr){
        tag[k]+=v;
        tree[k] += (r - l + 1) * v;
        return;
    }
    push_down(k, l, r);
    ll mid = (l + r) >> 1;
    if(cl<=mid)
        update(lson(k), l, mid, cl, cr, v);
    if(cr>mid)
        update(rson(k), mid + 1, r, cl, cr, v);
    push_up(k);
    return;
}
ll query(int k,int l,int r,int ql,int qr){
    ll res = 0;
    if(ql<=l&&r<=qr)
        return tree[k];
    push_down(k, l, r);
    int mid = (l + r) >> 1;
    if(ql<=mid)
        res += query(lson(k), l, mid, ql, qr);
    if(qr>mid)
       res += query(rson(k), mid + 1, r, ql, qr);
    return res;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <= n;++i)
        scanf("%lld",&a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m;++i){
        int opt=0;
        scanf("%d", &opt);
        if(opt==1){
            int x = 0, y = 0;
            ll k = 0;
            scanf("%d%d%lld",&x,&y,&k);
            update(1, 1, n, x, y, k);
        }
        else{
            int x=0,y=0;
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(1, 1, n, x, y));
        }
    }
    return 0;
}