模版_线段树

线段树模版之——区间修改与求和

题目描述:
给出数的个数n以及操作数q:
对于q:
1 x y z 令区间[x, y]增加z
2 x y 求区间和

#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxn=1e5+10;
int n, q;
LL a[maxn];
LL sum[maxn<<2], li[maxn<<2]; void _push_up(int rt) { sum[rt]=sum[rt<<1]+sum[(rt<<1)|1]; }//向上更新,求区间和 void _setup(int l, int r, int rt) { li[rt]=0; if(l==r){ sum[rt]=a[l]; return ; } int mid=(l+r)>>1; _setup(l, mid, rt<<1); _setup(mid+1, r, (rt<<1)|1); _push_up(rt); }//建树 void _push_down(int rt, int m) { if(li[rt]) { li[rt<<1]+=li[rt]; li[(rt<<1)|1]+=li[rt]; sum[rt<<1]+=li[rt]*(m-(m>>1)); sum[(rt<<1)|1]+=li[rt]*(m>>1); li[rt]=0; } }//将li标记向下更新,传递给小区间 void _add(int x, int y, LL z, int l ,int r, int rt) { if(x<=l && y>=r) { li[rt]+=z; sum[rt]+=z*(r-l+1); return ; } _push_down(rt, r-l+1); //边算边更新,节省复杂度 int mid=(l+r)>>1; if(x<=mid) _add(x, y, z, l, mid, rt<<1); if(y>mid) _add(x, y, z, mid+1, r, (rt<<1)|1); _push_up(rt); } LL _query(int x, int y, int l, int r, int rt) { if(x<=l && y>=r) { return sum[rt]; } _push_down(rt, r-l+1); //求和时再顺便更新一次 int mid=(l+r)>>1; LL ans=0; if(x<=mid) ans+=_query(x, y, l, mid, rt<<1); if(y>mid) ans+=_query(x, y, mid+1, r, (rt<<1)|1); //'夹'区间 return ans; } int main() { scanf("%d%d", &n, &q); for(int i=1; i<=n; i++) { scanf("%lld", a+i); } _setup(1, n, 1); for(int i=0; i<q; i++) { int ls; scanf("%d", &ls); if(ls==1) { int x, y; LL z; scanf("%d%d%lld", &x, &y, &z); _add(x, y, z, 1, n, 1); } else if(ls==2) { int x, y; scanf("%d%d", &x, &y); printf("%lld\n", _query(x, y, 1, n ,1)); } } return 0; }

有很多代码使用结构体实现线段树,不过我不太习惯用结构体来实现,因为有时候看到一堆点很难受...

猜你喜欢

转载自www.cnblogs.com/normaldisk/p/9126858.html