线段树第二天,下午由于做了多校的第一场virtual judge,所以直到晚上才有时间来搞这道题
除了早上做的那个区间染色的问题,这个问题应该也是个入门题,当然由于鄙人智商是硬伤,没太反应明白,查了解题报告,再加上自己的一些尝试,最终侥幸AC了,下面说说思路:
首先,更新区间的时候不可能更新区间里的每一个点,这样复杂度太高
因此,在update的时候,找到对应的区间之后,直接给该区间存储一个变化值,我放在了add数组中,同时,对每个节点还维护了一个和,存在sum数组中,表示区间内所有数的和。push_up函数就是对父节点就子节点的和,push_down是重点,因为更新是只更新到区间的,那么当下一次更新是对这个已经更新过的区间的子区间进行更新时,必须将他的变化量传到子节点中去,举个例子,假设我第一次对(2,6)区间加2,第二次对(3,4)区间加1,此时,在第一次操作后,只有(2,6)对应的add有值,其子区间对应的add都是为0,所以,第二次去更新的时候,先把这个值传递到子区间中去,然后再向下找,这是为了保证整棵树维护的信息的正确性,而在父节点向子节点push_down的时候,可以对子节点的sum进行一下更新,以方便查找,具体的看push_down函数就可以体会。在对该节点的子节点插入后,得更新该节点的信息,即push_up一下
下面说查询,查询和update其实差不度,如果找到区间了,就直接返回区间的和,否则,还是得先push_down一下,再对子节点进行递归
以下是代码:
#include <cstdio>
#include <cstring>
const int MAX = 100010;
long long sum[MAX<<2];
long long add[MAX<<2];
void push_up(int rt)
{
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int l,int r,int rt)
{
if(add[rt])
{
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
int mid = l+r>>1;
sum[rt<<1] += (mid-l+1)*add[rt];
sum[rt<<1|1] += (r-mid)*add[rt];
add[rt] = 0;
}
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
int mid = r+l>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(long long p,int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
add[rt]+=p;
sum[rt]+=p*(r-l+1);
return;
}
int mid = r+l>>1;
push_down(l,r,rt);
if(L<=mid) update(p,L,R,l,mid,rt<<1);
if(R>mid) update(p,L,R,mid+1,r,rt<<1|1);
push_up(rt);
}
long long query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return sum[rt];
int mid = r+l>>1;
long long ans = 0;
push_down(l,r,rt);
if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
return ans;
}
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)==2)
{
memset(add,0,sizeof(add));
build(1,n,1);
char c;
int x,y;
long long z;
while(k--)
{
scanf(" %c",&c);
if(c=='Q')
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(x,y,1,n,1));
}
else
{
scanf("%d%d%lld",&x,&y,&z);
update(z,x,y,1,n,1);
}
}
}
return 0;
}