题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66989#problem/C
题意:给定一个数组,然后动态修改某些区间的值,并询问某些区间的和。
思路:既然是线段树专题,那就用线段树了。这是一个标准的线段树维护动态区间和问题,很显然,我们需要维护的是这些连续区间的和,当我们对某个子区间进行add操作时,必然会对这个子区间的上一个区间的和值产生影响,也就是说,対小区间的操作会对大区间的和值产生影响。所以当我们修改一个区间[a,b]的值时,还得修改他的上面的间的和值,但是,[a,b]和他下面的区间的和值就不用在去访问了,我们可以再维护一个add值,表示每个区间的修改的值,这样,在query时,在累加他上面的区间的add就行了(大区间的add包含对子区间add)。这样一来,需要维护的变量就是add操作和sum值了。
代码:
#include<iostream>
#include<cstdio>
#define LL long long
#define maxn 100010
using namespace std;
struct node{
int l,r;
LL s,w;//记录该区间的和值,add操作
}T[maxn*3];
int N,Q,A[maxn];
LL build(int L,int R,int o){//初始化线段树
T[o].l=L;T[o].r=R;T[o].w=0;
if(L==R) return T[o].s=A[L];
return T[o].s=build(L,(L+R)/2,o*2)+build((L+R)/2+1,R,o*2+1);
}
void add(int L,int R,int val,int o){
if(T[o].l==L && T[o].r==R){//注意这里用的是==号
T[o].w+=val;return;//记录该区间的add操作,但是可以不用更新该区间的和值
}
T[o].s+=(R-L+1)*val;//[L,R]一定是o对应区间的子区间,所以的更新o的和值
if(T[o].l==T[o].r) return;
if(L>=T[o*2+1].l) add(L,R,val,o*2+1);
else if(R<=T[o*2].r) add(L,R,val,o*2);
else{
add(L,T[o*2].r,val,o*2);add(T[o*2+1].l,R,val,o*2+1);
}
}
LL query(int L,int R,LL val,int o){
if(T[o].l>=L && T[o].r<=R) return T[o].s+(T[o].r-T[o].l+1)*(T[o].w+val);
if(T[o].l==T[o].r) return 0;
if(L>=T[o*2+1].l) return query(L,R,val+T[o].w,o*2+1);
if(R<=T[o*2].r) return query(L,R,val+T[o].w,o*2);
return query(L,T[o*2].r,val+T[o].w,o*2)+query(T[o*2+1].l,R,val+T[o].w,o*2+1);
}
int main(){
//freopen("D:\\in.txt","r",stdin);
while(cin>>N){
cin>>Q;
for(int i=1;i<=N;i++)
scanf("%d",A+i);
build(1,N,1);
int a,b,c;char ch;
while(Q--){
getchar();
scanf("%c",&ch);
if(ch=='Q'){
scanf("%d %d",&a,&b);
printf("%lld\n",query(a,b,0,1));
}else{
scanf("%d %d %d",&a,&b,&c);
add(a,b,c,1);
}
}
}
return 0;
}