【演算法】線段樹
阿新 • • 發佈:2018-12-02
之前由於不會延遲標記一直沒寫這題。
那麼今天就寫一個總結來加深印象吧!
首先,延遲標記的作用就是在於當某個區間遭到改變,但又用不上(不用輸出)時。將所有先前改變都標記上去,等到要用到了,一次直接改。
#include <bits/stdc++.h> #define maxn 100005 using namespace std; struct segment_tree{ int l,r; long long v,add;//加一個延遲標記的變數 }t[maxn*4]; long long a[4*maxn]; void build_up(int x,int l,int r){ int mid=(l+r)/2; t[x].l=l;t[x].r=r; if (l==r){t[x].v=a[l];return ;} build_up(x*2,l,mid); build_up(x*2+1,mid+1,r); t[x].v=t[x*2].v+t[x*2+1].v;//標準建樹 } void spread(int x){ if (t[x].add){ t[x*2].v+=t[x].add*(t[x*2].r-t[x*2].l+1); t[x*2+1].v+=t[x].add*(t[x*2+1].r-t[x*2+1].l+1);//更改區間值 t[x*2].add+=t[x].add;//兩個子樹加上標記 t[x*2+1].add+=t[x].add; t[x].add=0;//清空當前標記 } } void change(int x,int l,int r,long long d){ int mid=(t[x].l+t[x].r)/2; if (l<=t[x].l&&r>=t[x].r){//若此節點被整個更改區間覆蓋 t[x].v+=d*(t[x].r-t[x].l+1); t[x].add+=d;//(接上)則可以直接更改它的值,並加上標記 return ; } spread(x);//若沒有,則直接向下改 if (l<=mid) change(x*2,l,r,d); if (r>mid) change(x*2+1,l,r,d); t[x].v=t[x*2].v+t[x*2+1].v;//這時再改值 } long long ask(int x,int l,int r){ if (l<=t[x].l&&r>=t[x].r)return t[x].v;//如果被當前區間覆蓋,就可以直接輸出值了 spread(x);//若沒有,則向下標記 int mid=(t[x].l+t[x].r)/2; long long val=0; if (l<=mid) val+=ask(x*2,l,r); if (r>mid) val+=ask(x*2+1,l,r); return val; } //void merge(int x,int y){ // spread(t[x].l,t[x].r); // if (t[x].l!=t[x].r) //} int main(){ int i,m,n; cin>>n>>m; for (i=1;i<=n;i++) cin>>a[i]; build_up(1,1,n); for (i=1;i<=m;i++){ int x,y,z; long long k; cin>>x>>y>>z; if (x==1){ cin>>k; change(1,y,z,k); } else cout<<ask(1,y,z)<<endl; } return 0; }