線段樹---(區間修改區間查詢)
阿新 • • 發佈:2019-02-19
例題POJ3468 ----模板題
原理詳解:
首先 大家應該都已經會了線段樹的 單點更新和單點查詢了 也就是已經瞭解了線段樹的整體機制
那麼 線段樹的區間操作呢 主要是使用了一個延遲標記
lazy標記(延遲標記、懶惰標記)
通過線段樹的區間查詢和單點修改,我們知道,我們建立一棵二叉樹,每個節點代表一個區間,葉子節點代表一個數,而我們單點修改的時候,只要從根出發自上向下,在log級別的複雜度內就可以找到我們要修改的點,然後修改這個點後,從葉子開始,自下向上把和其有關(受到影響)的節點全部修改。
那麼區間修改,就是利用線段樹裡面每一個節點代表一個區間的特點,我們不需要去修改區間裡的每一個值,而是在我們想要修改的區間對應的節點打上一個修改標記,也就是lazy標記,而在詢問區間和的時候,檢查所詢問的區間的lazy標記,然後乘上原來的區間和,就得到了答案。
AC程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=1e5+7; typedef long long ll; ll sum[maxn<<2],add[maxn<<2]; struct Node{ int l,r; int mid(){ return (l+r)>>1; } }tree[maxn<<2]; void PushUp(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void PushDown(int rt,int m){ if(add[rt]){ add[rt<<1]+=add[rt]; add[rt<<1|1]+=add[rt]; sum[rt<<1]+=add[rt]*(m-(m>>1)); sum[rt<<1|1]+=add[rt]*(m>>1); add[rt]=0; } } void BuildTree(int l,int r,int rt){ tree[rt].l=l; tree[rt].r=r; add[rt]=0; if(l==r){ scanf("%I64d",&sum[rt]);return ; } int m=tree[rt].mid(); BuildTree(l,m,rt<<1); BuildTree(m+1,r,rt<<1|1); PushUp(rt); } void Update(int c,int l,int r,int rt){ if(tree[rt].l==l&&tree[rt].r==r){ add[rt]+=c; sum[rt]+=(ll)c*(r-l+1); return ; } if(tree[rt].l==tree[rt].r) return ; PushDown(rt,tree[rt].r-tree[rt].l+1); int m=tree[rt].mid(); if(r<=m) Update(c,l,r,rt<<1); else if(l>m) Update(c,l,r,rt<<1|1); else{ Update(c,l,m,rt<<1); Update(c,m+1,r,rt<<1|1); } PushUp(rt); } ll Query(int l,int r,int rt){ if(tree[rt].l==l&&tree[rt].r==r) return sum[rt]; PushDown(rt,tree[rt].r-tree[rt].l+1); int m=tree[rt].mid(); ll res=0; if(r<=m) res+=Query(l,r,rt<<1); else if(l>m) res+=Query(l,r,rt<<1|1); else{ res+=Query(l,m,rt<<1); res+=Query(m+1,r,rt<<1|1); } return res; } int n,m,x,y,z; char ch[3]; int main(){ while(~scanf("%d%d",&n,&m)){ BuildTree(1,n,1); while(m--){ scanf("%s",ch); if(ch[0]=='Q'){ scanf("%d%d",&x,&y); printf("%lld\n",Query(x,y,1)); } else{ scanf("%d%d%d",&x,&y,&z); Update(z,x,y,1); } } } return 0; }