A Simple Problem with Integers -POJ 3468
A Simple Problem with Integers
**POJ 3468**
題目大意
有n個數A1 A2 A3 … An,q個操作。N and Q. 1 ≤ N,Q ≤ 100000.
每個詢問支援兩種操作
C a b c 將Aa 到 Ab都加上c
Q a b 求Aa…Ab的區間和
-1000000000 ≤ Ai ≤ 1000000000.
-10000 ≤ c ≤ 10000.
題目分析
非常簡單的一道線段樹區間修改模板題。
(樹狀陣列也可做只不過更麻煩)
主要是用於入門和練手。
我們用一棵線段樹來維護區間和,對於區間修改的操作,如果我們樸素地使用修改的方式逐一對包含的結點進行更新,那麼最差的情況下可能會導致所有的結點都發生改變而更新,這樣我們修改一次的複雜度就退化到了O(n),顯然是無法接受的。
試想,如果我們在一次修改指令的過程中發現結點p代表的區間[pl,pr]被修改區間[l,r]完全覆蓋,並且逐一更新了子樹p中所有的結點,但是可能在之後的查詢中卻根本不需要用到[pl,pr]的子區間作為候選答案,那麼更新p的所有子樹就是徒勞的。
換句話說,如果當前結點p被完全包含在了[l,r]中,那麼我們可以立即返回,不過我們在返回之前需要在p上留下一個標記,表示該節點曾經被修改過,但它的子節點暫時還未更改
這就是常見的懶惰標記思想。
如果在後續的操作中,我們需要訪問這個結點p的子節點,也就是需要從p向下遞迴,那麼我們可以檢查p上是否含有這個懶惰標記,如果有標記,那麼我們就根據標記資訊更新p的兩個子節點,同時給兩個子節點打上標記,再將p的標記清除。這樣就讓每次的遞迴都是用在了有用的結點上。
換句話說,除了在對於這個區間的操作分成的O(logn)個線段的結點進行直接修改外,
對別的任意的節點的修改都延遲到**在後續操作中遞迴進入到它的父節點並且需要繼續向下遞迴時**再進行。
這樣一來,每次查詢或者修改區間的操作複雜度都是O(nlogn)。
延遲標記提供了線段樹中從上向下傳遞資訊的方法。
同時需要注意的時,一個節點被打上“懶惰標記”的同時,它自身的儲存資訊已經被修改完畢。
同時傳遞完標記並且遞迴完,應該重新計算維護當前節點的資訊。
同時資料較大應該使用long long
程式碼
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define LL long long const int MAXN=100005; struct Node { LL sum,addv; }tree[MAXN<<2]; int n,m; void build_tree(int o,int l,int r) { if(l==r) { int x; scanf("%d",&x); tree[o].sum=x; return; } int mid=(l+r)>>1; build_tree(o<<1,l,mid); build_tree(o<<1|1,mid+1,r); tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum; } void push_down(int o,int l,int r) { if(!tree[o].addv) return; int lc=(o<<1),rc=(o<<1|1); int mid=(l+r)>>1; tree[lc].addv+=tree[o].addv; tree[rc].addv+=tree[o].addv; tree[lc].sum+=tree[o].addv*(mid-l+1); tree[rc].sum+=tree[o].addv*(r-mid); tree[o].addv=0; } LL query(int o,int l,int r,int x,int y) { if(x<=l&&y>=r) return tree[o].sum; push_down(o,l,r); LL ans=0; int mid=(l+r)>>1; if(x<=mid) ans+=query(o<<1,l,mid,x,y); if(y>mid) ans+=query(o<<1|1,mid+1,r,x,y); return ans; } void change(int o,int l,int r,int x,int y,int p) { if(x<=l&&y>=r) { tree[o].sum+=(LL)(r-l+1)*p; tree[o].addv+=p; return; } push_down(o,l,r); int mid=(l+r)>>1; if(x<=mid) change(o<<1,l,mid,x,y,p); if(y>mid) change(o<<1|1,mid+1,r,x,y,p); tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum; } void init() { cin>>n>>m; build_tree(1,1,n); } void work() { char k; int x,y,z; while(m--) { cin>>k; if(k=='Q') { scanf("%d%d",&x,&y); printf("%lld\n",query(1,1,n,x,y)); } else { scanf("%d%d%d",&x,&y,&z); change(1,1,n,x,y,z); } } } int main() { init(); work(); return 0; }