①對於要求的輸出,須要維護哪些信息(線段樹節點裏要存什麼)?
②這些信息是否能夠直接獲得?
③若是不能,須要維護哪些信息來間接得到?node
兩件事:由上而下幹什麼? 由下而上幹什麼?
由上而下的過程——初始化
由下而上的過程——合併區間信息
從上往下跑一圈又必需要倒回去的算法——遞歸。ios
那麼就是遞歸實現的嘍。git
理論上是2n-1的空間,可是你遞歸創建的時候當前節點爲r
,那麼左右孩子分別是2r,2r+1,此時編譯器並不知道遞歸已結束,
由於你的結束條件是在遞歸以前的,因此編譯器會認爲下標訪問出錯,也就是空間開小了,
應該再開大2倍。有時候可能你發現開2,3倍的空間也能夠AC,那只是由於測試數據並無那麼大。算法
主要的思路就是遞歸加二分
看看代碼吧(~~懷念那學長看着我打代碼,強行改我碼風的日子)測試
void build(ll l,ll r,ll rt) { t[rt].len = r - l + 1; if(l == r) { t[rt].sum = read(); return; } ll m = (l + r) >> 1; build(l, m, lson); build(m + 1, r ,rson); pushup(rt); }
知道二分與遞歸以後,單點查詢那不就成了一個,二分求解的過程了嘛ui
void ask(int k) { if(tree[k].l==tree[k].r) //當前結點的左右端點相等,那麼說明是葉子節點,是最終答案 { ans=tree[k].w; return ; } int m=(tree[k].l+tree[k].r)/2; // 二分查找 if(x<=m) ask(k*2); else ask(k*2+1); }
與單點查詢沒有什麼區別,就是加了修改操做
還有一個回溯的過程this
void add(int k) { if(tree[k].l==tree[k].r)//找到目標位置 { tree[k].w+=y; return; } int m=(tree[k].l+tree[k].r)/2; if(x<=m) add(k*2); else add(k*2+1); tree[k].w=tree[k*2].w+tree[k*2+1].w;//全部包含結點k的結點狀態更新 }
由於線段樹的時候,有一些節點雖然咱們下放了值了,也加上了,可是咱們並無用到這個地方
那麼咱們能夠如今查詢到的地方打上一個lazy標記(稱爲懶標記),而後每次查到這裏的時候
就將當前的區間維護的值,與lazy標記處理一下就行了,這樣咱們就沒有必要再向下跑到葉子節點
而後在合併區間信息了,很麻煩,也很慢...spa
當他區間修改的時候,無非就是三種狀況code
like this
blog
like this
like this
看一下代碼吧
void updata(ll L,ll R,ll c,ll l,ll r,ll rt){ if(L <= l && r <= R){ t[rt].sum += t[rt].len * c; t[rt].lazy += c; return ; } pushdown(rt); ll m = (l + r) >> 1; if(L <= m) updata(L,R,c,l,m,lson); if(R > m) updata(L,R,c,m + 1,r,rson); pushup(rt); }
ll query(ll L,ll R,ll l,ll r,ll rt){ if(L <= l && r <= R) return t[rt].sum; pushdown(rt); ll m = (l + r) >> 1,ans=0; if(L <= m) ans+=query(L, R, l, m, lson); if(R > m) ans+=query(L, R, m + 1, r, rson); return ans; }
最後合併總的代碼(由於單點查詢與單點修改能夠仿照區間查詢與區間修改上來我就不放了)
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<algorithm> #include<map> #include<set> #include<vector> #include<string> #define ll long long #define N 1000010 #define lson rt << 1 #define rson rt << 1 | 1 using namespace std; ll n,m,rt; struct node { ll sum,lazy,len; } t[N << 2 | 1]; ll read() { ll s=0,f=0; char ch=getchar(); while(!isdigit(ch)) f|=(ch=='-'),ch=getchar(); while(isdigit(ch)) s=s*10+(ch^48),ch=getchar(); return f?-s:s; } void build(ll l,ll r,ll rt); void pushup(ll rt); void pushdown(ll rt); void updata(ll L,ll R,ll c,ll l,ll r,ll rt); ll query(ll L,ll R,ll l,ll r,ll rt); int main() { n = read(), m = read(); build(1,n,1); for(ll i = 1, opt, l, r, k; i <= m; i++){ opt = read(),l = read(),r = read(); if(opt == 1){k = read();updata(l,r,k,1,n,1);} else printf("%lld\n",query(l,r,1,n,1)); } return 0; } void build(ll l,ll r,ll rt) { t[rt].len = r - l + 1; if(l == r) { t[rt].sum = read(); return; } ll m = (l + r) >> 1; build(l, m, lson); build(m + 1, r ,rson); pushup(rt); } void pushup(ll rt) { t[rt].sum = t[lson].sum + t[rson].sum; } void pushdown(ll rt) { if(t[rt].lazy) { t[lson].lazy += t[rt].lazy; t[rson].lazy += t[rt].lazy; t[lson].sum += t[lson].len * t[rt].lazy; t[rson].sum += t[rson].len * t[rt].lazy; t[rt].lazy = 0; } } void updata(ll L,ll R,ll c,ll l,ll r,ll rt){ if(L <= l && r <= R){ t[rt].sum += t[rt].len * c; t[rt].lazy += c; return ; } pushdown(rt); ll m = (l + r) >> 1; if(L <= m) updata(L,R,c,l,m,lson); if(R > m) updata(L,R,c,m + 1,r,rson); pushup(rt); } ll query(ll L,ll R,ll l,ll r,ll rt){ if(L <= l && r <= R) return t[rt].sum; pushdown(rt); ll m = (l + r) >> 1,ans=0; if(L <= m) ans+=query(L, R, l, m, lson); if(R > m) ans+=query(L, R, m + 1, r, rson); return ans; }