Luogu P3372 【模板】線段樹 1
阿新 • • 發佈:2020-07-26
思路
線段樹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; }