题目链接:https://www.luogu.com.cn/problem/P3372
题意:
题解:如果采用单点更新的思路对区间进行更新的话时间复杂度会比较高,因此用了一个lazy数组(俗称懒人标记),它为什么叫懒人标记呢,比如更新的区间为[l,r]+z,而l到r覆盖了线段树某一个节点的区间,那么可以将该节点对应的lazy数组的值加上z,同时更新该节点的值,这样就可以避免更新它子孙节点的值了,当要查询或者修改的时候要将lazy标记往下传一层,这样就能保证当在更新或查询的时候子孙节点的值是正确的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
ll a[maxn],tree[maxn],lazy[maxn];
int n,m;
void build(int node,int start,int end){ //建树
if(start==end){
tree[node]=a[start];return;
}
int mid=(start+end)/2;
build(node*2,start,mid);
build(node*2+1,mid+1,end);
tree[node]=tree[node*2]+tree[node*2+1];
}
void pushdown(int node,int tot){ //将lazy标记向下传递
lazy[node*2]+=lazy[node];
lazy[node*2+1]+=lazy[node];
tree[node*2]+=lazy[node]*(tot-tot/2);
tree[node*2+1]+=lazy[node]*(tot/2);
lazy[node]=0;
}
void update(int node,int start,int end,int x,int y,ll z){ //更新
if(start>=x&&end<=y){
lazy[node]+=z;
tree[node]+=z*(end-start+1);
return;
}
pushdown(node,end-start+1);
int mid=(start+end)/2;
if(x<=mid) update(node*2,start,mid,x,y,z);
if(y>mid) update(node*2+1,mid+1,end,x,y,z);
tree[node]=tree[node*2]+tree[node*2+1];
}
ll query(int node,int start,int end,int l,int r){ //查询
if(l>end||r<start) return 0;
if(start>=l&&end<=r) return tree[node];
pushdown(node,end-start+1);
int mid=(start+end)/2;
return query(node*2,start,mid,l,r)+query(node*2+1,mid+1,end,l,r);
}
int main()
{
int op,x,y;ll k;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--){
cin>>op;
if(op==1){
cin>>x>>y>>k;
update(1,1,n,x,y,k);
}
else{
cin>>x>>y;
cout<<query(1,1,n,x,y)<<endl;
}
}
return 0;
}