線段樹小記
阿新 • • 發佈:2020-07-23
普通線段樹可以[先標記\(tag\)後更改]或[邊推\(tag\)邊更改]
打算從原來的[先標記\(tag\)後更改]改變為較為普及的[邊推\(tag\)邊更改]
這裡放模板題程式碼
#include<cstdio> #include<iostream> #define N 100005 #define ll long long using namespace std; struct node { int l,r; ll s,lz; }t[N << 2]; ll a[N]; int n,m,opt,x,y; ll z; void merge(int p) { t[p].s=t[p+p].s+t[p+p+1].s; } void build(int p,int l,int r) { t[p].l=l,t[p].r=r; if (l==r) { t[p].s=a[l]; t[p].lz=0; return; } int mid=(l+r) >> 1; build(p+p,l,mid); build(p+p+1,mid+1,r); merge(p); } void down_tag(int p,ll z) { t[p].s+=z*(t[p].r-t[p].l+1); t[p].lz+=z; } void down(int p) { down_tag(p+p,t[p].lz),down_tag(p+p+1,t[p].lz); t[p].lz=0; } void change(int p,int x,int y,ll z) { if (t[p].l>y||t[p].r<x) return; if (t[p].l>=x&&t[p].r<=y) { t[p].s+=z*(t[p].r-t[p].l+1); t[p].lz+=z; return; } down(p); change(p+p,x,y,z); change(p+p+1,x,y,z); merge(p); } ll calc(int p,int x,int y) { if (t[p].l>y||t[p].r<x) return 0; if (t[p].l>=x&&t[p].r<=y) return t[p].s; down(p); return calc(p+p,x,y)+calc(p+p+1,x,y); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); for (int i=1;i<=m;i++) { scanf("%d",&opt); switch (opt) { case 1: scanf("%d%d%lld",&x,&y,&z); change(1,x,y,z); break; case 2: scanf("%d%d",&x,&y); printf("%lld\n",calc(1,x,y)); break; } } }