【模板】线段树 区间加,区间求和 (模板题:P3372线段树1)

来源:互联网 发布:手机视频剪辑软件知乎 编辑:程序博客网 时间:2024/06/05 07:10

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1:
5 51 5 4 2 32 2 41 2 3 22 3 41 1 5 12 1 4
输出样例#1:
11820








说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^,保证在int64/long long数据范围内)

样例说明:




#include <iostream>#include <cstdio>#define ll long longusing namespace std;ll n,m,flag,x,y,v;ll add[2100000],sum[2100000],a[2100000];void pushup(ll rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void build(ll l,ll r,ll rt){if (l==r){sum[rt]=a[l];return ;}ll m=(l+r)>>1;build(l,m,rt<<1);build(m+1,r,rt<<1|1);pushup(rt);}void pushdown(ll rt,ll ln,ll rn){if (add[rt]){add[rt<<1]+=add[rt];add[rt<<1|1]+=add[rt];sum[rt<<1]+=add[rt]*ln;sum[rt<<1|1]+=add[rt]*rn;add[rt]=0;}}void update(ll L,ll R,ll c,ll l,ll r,ll rt){if (L<=l && r<=R){sum[rt]+=c*(r-l+1);add[rt]+=c;return ;}ll m=(l+r)>>1;pushdown(rt,m-l+1,r-m);if (L<=m) update(L,R,c,l,m,rt<<1);if (R> m) update(L,R,c,m+1,r,rt<<1|1);pushup(rt);}ll ask(ll L,ll R,ll l,ll r,ll rt){if (L<=l && r<=R){return sum[rt];}ll m=(l+r)>>1;pushdown(rt,m-l+1,r-m);ll s=0;if (L<=m) s+=ask(L,R,l,m,rt<<1);if (R >m) s+=ask(L,R,m+1,r,rt<<1|1);return s;}int main(){scanf("%lld%lld",&n,&m);for (ll i=1;i<=n;++i)scanf("%lld",&a[i]);build(1,n,1);for (ll i=1;i<=m;++i){scanf("%lld",&flag);if (flag==1){scanf("%lld%lld%lld",&x,&y,&v);update(x,y,v,1,n,1);}elseif (flag==2){scanf("%lld%lld",&x,&y);printf("%lld\n",ask(x,y,1,n,1));}}return 0;}