题意:
给出 N 个数 ,Q 个查询 ,区间更新,区间查询
数据范围:
1 < = N , Q < = 1 0 5 1<=N,Q<=10^5 1<=N,Q<=105 − 1 e 9 < = A i < = 1 e 9 -1e^9<=Ai<=1e^9 −1e9<=Ai<=1e9
对 lazy 标志的理解::
- 在update 的时候,当要更新的区间[A,B] 完全包含了 segment 的左右的端点时,不用接着再往下更新它的子节点的信息,因为如果再接着更新下去,设 lazy 数组就没有意义了,就相当于单点更新整段 [A,B] 一样。
- 而拿到此次 lazy 标志的点,它的 sum 已经更新了, lazy 的作用是延迟了对子节点 sum 的更新,就好像大哥说:“小弟们,上头下来的钱我先给你们保管了。而当外界的人想要来询问小弟身价时,因为必要经过大哥点来找到小弟,这时大哥才把小弟们应有的钱 ( lazy 信息) pushdown下去。”
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ls x<<1
#define rs x<<1|1
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N];
struct tnode{
ll l,r,sum,lazy;
}t[N<<2]; //节点开至少 4 倍空间
void pushup(int x)
{
t[x].sum=t[ls].sum+t[rs].sum;
}
void pushdown(int x)
{
int mid=(t[x].l+t[x].r)>>1;
if(t[x].lazy)//有标记就下传
{
t[ls].lazy+=t[x].lazy;
t[rs].lazy+=t[x].lazy;
t[ls].sum+=(mid-t[x].l+1)*t[x].lazy;
t[rs].sum+=(t[x].r-mid)*t[x].lazy;
t[x].lazy=0;
}
}
void build(int l,int r,int x)
{
t[x].l=l, t[x].r=r, t[x].lazy=0; //可以在建树时给 lazy 赋值,我觉得这样更结构化
if(l==r)
{
t[x].sum=a[l]; // l == r 叶子节点
return;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
pushup(x); //建完两个子节点后,要更新父节点的值,这对父节点的 sum 有影响
}
void update(int A,int B,int val,int x)
{
if(A<= t[x].l && t[x].r<=B)
{
t[x].sum+=(t[x].r-t[x].l+1)*val;
t[x].lazy+=val;
return;
}
//没有完全覆盖,就去更新[A,B]和两个子节点有交集的部分
pushdown(x);
int mid=(t[x].l+t[x].r)>>1;
if(A<=mid)
update(A,B,val,ls);
if(mid<B)
update(A,B,val,rs);
pushup(x); //两子节点的sum值变了,父节点的sum值也要变
}
ll query(int A,int B,int x)
{
if(A<= t[x].l && t[x].r<=B) //t[x] 的整段贡献给 ans
return t[x].sum;
pushdown(x); //询问的点在 x 的子节点,也是要同步看是否lazy标记,因为可能要查询的节点的sum的更新信息被上一个节点延迟了。
ll ans=0, mid=(t[x].l+t[x].r)>>1;
if(A<=mid)
ans+=query(A,B,ls);
if(mid<B)
ans+=query(A,B,rs);
return ans;
}
int main()
{
ll n,q,u,v,c;
char ch;
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,n,1);
while(q--)
{
getchar();
scanf("%c %lld %lld",&ch,&u,&v);
if(ch=='Q')
printf("%lld\n",query(u,v,1));
else
{
scanf("%lld",&c);
update(u,v,c,1);
}
}
return 0;
}