蒟蒻在线段树的路上继续前进,lazy的出题人的奇思妙想——延迟标记震撼了他。。
线段树模板题(二)——区间修改与查询区间和:区间修改区间求和
【题目描述】
如题,已知一个数列,你需要进行下面两种操作:
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的结果。
【样例输入】
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
【样例输出】
11
8
20
代码:
#include<bits/stdc++.h>
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
long long n,m,a[100010];
struct node{
int l,r,lazy;//延迟标记——lazy
long long sum;
}T[100010*4];
void pushnow(long long p,long long v){
T[p].sum+=(T[p].r-T[p].l+1)*v;
T[p].lazy+=v;
}
void pushdown(long long p){
if(T[p].lazy){
pushnow(lc,T[p].lazy);
pushnow(rc,T[p].lazy);
T[p].lazy=0;
}
}
void pushup(long long p){
T[p].sum=T[lc].sum+T[rc].sum;
}
void build(long long p,long long l,long long r){//建树
T[p].l=l;
T[p].r=r;
if(l==r){
T[p].sum=a[l];
T[p].lazy=0;
return ;
}
long long mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(p);
}
void update(long long p,long long ql,long long qr,long long v){//向上维护
if(ql<=T[p].l&&T[p].r<=qr){
pushnow(p,v);
return ;
}
long long mid=(T[p].l+T[p].r)>>1;
pushdown(p);
if(ql<=mid)
update(lc,ql,qr,v);
if(qr>mid)
update(rc,ql,qr,v);
pushup(p);
}
long long query(long long p,long long ql,long qr){//区间求和
if(ql<=T[p].l&&qr>=T[p].r)
return T[p].sum;
long long mid=(T[p].l+T[p].r)>>1;
pushdown(p);
long long ans=0;
if(ql<=mid)
ans+=query(lc,ql,qr);
if(qr>mid)
ans+=query(rc,ql,qr);
pushup(p);
return ans;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,100100);
for(long long i=1;i<=m;i++){
long long p;
scanf("%lld",&p);
if(p==1){
long long x,y,k;
scanf("%lld%lld%lld",&x,&y,&k);
update(1,x,y,k);
}
if(p==2){
long long x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,x,y));
}
}
return 0;
}