要求:有两个操作,Q是询问一段区间的和,C是将一段区间的所有数加上一个数。
方法:线段树模板题。
1.定义addv[i]为第i个结点对应的区间加上addv[i],但是先不将addv[i]下放给子孙,这样可以减小时间复杂度。
2.定于sumv[i]为第i个结点对应的区间的和。
3.pushdown函数将addv[i]下方给第i个结点的孩子,并更新子孙的sumv数组。
4.pushup函数更新父亲的sumv。
5.update数组更新addv,下放pushdown和上去pushup不会更新所有的结点。
6.由5知查询时需继续进行pushdown,无需进行pushup。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
int k,n,Q,a,b,c;
long long add,sumv[1000005],addv[1000005],sum1;
void pushdown(int i,int ln,int rn)
{
sumv[i*2]+=addv[i]*ln;
sumv[i*2+1]+=addv[i]*rn;
addv[i*2]+=addv[i];
addv[i*2+1]+=addv[i];
addv[i]=0;
}
void pushup(int i)
{
sumv[i]=sumv[i*2]+sumv[i*2+1];
}
void query(int i,int L,int R)
{
int M=L+(R-L)/2;
if(a<=L&&R<=b)
sum1+=sumv[i];
else
{
pushdown(i,M-L+1,R-M);//下去add
if(a<=M) query(i*2,L,M);
if(M<b) query(i*2+1,M+1,R);
}
}
void update(int i,int L,int R)
{
int lc=i*2,rc=i*2+1;
int M=L+(R-L)/2;
if(a<=L&&R<=b)//addv 存的是区间+add
{
sumv[i]+=c*(R-L+1);
addv[i]+=c;
return;
}
pushdown(i,M-L+1,R-M);//下去add
if(a<=M) update(lc,L,M);
if(b>M) update(rc,M+1,R);
pushup(i);//下去add
}
int main()
{
int i;
char s;
scanf("%d%d",&n,&Q);
memset(sumv,0,sizeof(sumv));
memset(addv,0,sizeof(addv));
for(i=1;i<=n;i++)
{
scanf("%d",&c);
a=b=i;
update(1,1,n);
}
memset(addv,0,sizeof(addv));
for(i=0;i<Q;i++)
{
getchar();
scanf("%c",&s);
if(s=='Q')
{
scanf("%d%d",&a,&b);
sum1=0;
query(1,1,n);
printf("%lld\n",sum1);
}
else
{
scanf("%d%d%d",&a,&b,&c);
update(1,1,n);
}
}
}