一:定義html
首先要明確線段樹的定義,線段樹是一顆樹,並且是徹底二叉樹。同時線段樹的每一個節點表示一個區間,左子樹和右子樹分別表示這個區間的左半邊和右半邊。算法
即將區間[L,R]分解成[L,MID]和[MID+1,R],假設根的高度爲1,樹高爲(n>1)ui
下圖展現了區間[1,13]的分解過程spa
二:原理htm
上圖中每一個節點存儲本身對應區間的信息。blog
(1)單點修改遞歸
假設要修改1號節點,不難發現只要修改[1,13]、[1,7]、[1,4]、[1,2]、[1,1]這幾個節點的信息,也就是更新1號節點到根節點的這條鏈上的全部信息get
因此最大修改次數就是樹的高度class
(2)區間查詢變量
咱們能夠將一個[1,n]的線段樹分紅若干個連續不相交區間,嗯大概是
詳細證實能夠參考https://www.cnblogs.com/AC-King/p/7789013.html
(3)區間修改
樸素算法:把區間內的每一個點單點修改(好像有那麼一點點...暴力)
那該怎麼辦呢,這時候一個神奇的東西出現了——懶惰標記(Lazy tag)
咱們把對節點的修改狀況儲存在標記裏,在訪問一個節點的時候,「順便」把它的標記傳遞給它的兒子節點,也就是懶惰標記的下放。
實現思路(重點)
<1>增長一個新的變量用來存儲Lazy tag
<2>遞歸到這個節點時,只更新這個節點的狀態,並把當前的更改值累積到標記裏
<3>當須要遞歸這個節點的子節點的時候,標記下傳給子節點
①當前節點的懶惰標記累積到子節點的懶惰標記中
②修改子節點狀態(原狀態+子節點區間大小*父節點傳下來的懶惰標記)
③父節點懶惰標記清零
三:代碼實現
const int MAXN=50010; int a[MAXN],ans[MAXN<<2],lazy[MAXN<<2]; //a[]爲原序列信息,ans[]模擬線段樹維護區間和,lazy[]爲懶惰標記 void PushUp(int rt) { ans[rt]=ans[rt<<1]+ans[rt<<1|1]; } void Build(int l,int r,int rt) { if (l==r) { ans[rt]=a[l]; return; } int mid=(l+r)>>1; Build(l,mid,rt<<1); Build(mid+1,r,rt<<1|1); PushUp(rt); } void PushDown(int rt,int ln,int rn)//ln表示左子樹元素結點個數,rn表示右子樹結點個數 { if (lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; ans[rt<<1]+=lazy[rt]*ln; ans[rt<<1|1]+=lazy[rt]*rn; lazy[rt]=0; } } void Add(int L,int C,int l,int r,int rt) { if (l==r) { ans[rt]+=C; return; } int mid=(l+r)>>1; //PushDown(rt,mid-l+1,r-mid); 若既有點更新又有區間更新,須要這句話 if (L<=mid) Add(L,C,l,mid,rt<<1); else Add(L,C,mid+1,r,rt<<1|1); PushUp(rt); } void Update(int L,int R,int C,int l,int r,int rt) { if (L<=l&&r<=R) { ans[rt]+=C*(r-l+1); lazy[rt]+=C; return; } int mid=(l+r)>>1; PushDown(rt,mid-l+1,r-mid); if (L<=mid) Update(L,R,C,l,mid,rt<<1); if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1); PushUp(rt); } LL Query(int L,int R,int l,int r,int rt) { if (L<=l&&r<=R) return ans[rt]; int mid=(l+r)>>1; PushDown(rt,mid-l+1,r-mid);//若更新只有點更新,不須要這句 LL ANS=0; if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1); if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1); return ANS; } int main() { //建樹 Build(1,n,1); //點更新 Add(L,C,1,n,1); //區間修改 Update(L,R,C,1,n,1); //區間查詢 int ANS=Query(L,R,1,n,1); }