题目:http://poj.org/problem?id=3468
解题思路:本题是一个裸的线段树,这里不过多赘述。
其实可以用树状数组来做,我们熟知的树状数组一般是单点更新,区间查询,但树状数组也可以进行区间更新。
设要更新的区间是[l,r]
1、在l<=pos<=r时,前缀和的增量为val=(pos-l+1)*x
2、在pos>r时,前缀和增量为val=(r-l+1)*x
观察上述两式,我们可以看到公共增量-(l-1)*x,因此我们可以通过单点更新将该值加在pos>=l的前缀和上。
对于(2)式,剩余部分为r*x,同理,我们可以通过单点更新将该值加到pos>=r的前缀和上。
那么现在只剩下一部分,即对于l<=pos<=r中每一个pos,应该加上一个大小为pos*x的增量,但pos是变化的,所以我们其实可以在查询的时候为每个pos增加这个增量。
现在看来所有的处理已经结束了。其实不然,单点更新影响的是某点之后所有的前缀和,所以在更新pos*x的时候,pos>j的区间已经被破坏掉了。
现在我们可以考虑另一种做法,将该增量存储下来,作为lazytag,每次查询的时候再进行计算。
那处理的时候需要两个操作:
1、lazy[l]+=x;//对pos>=l的所有节点加x
2、lazy[r+1]-=x;//对pos>r的所有节点减x
每次查询的时候,对于区间[L,R],其求解公式为getSum(R)+getLazy(R)*R-getSum(L-1)-getLazy(L-1)(L-1)
其实这个式子很好理解,若是R在更新的区间内,L不在更新的区间内,那么结果就是R*x,此时getLazy(L-1)=0;若是R在,L也在,那么答案就是他们相差的数目cnt*x;若是R不在,L在,那R∈[r,无穷],进行计算的时候这部分不需要考虑增量的情况,但要减去L的那部分。
以上就是树状数组区间更新。
下面附AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
#define ll long long
using namespace std;
ll lazy[N],data[N];
int lowbit(int x){
return x&(-x);
}
void update(int pos,ll val){
while(pos<N){
data[pos]+=val;
pos+=lowbit(pos);
}
}
ll getSum(int pos){
ll sum=0;
while (pos>0){
sum+=data[pos];
pos-=lowbit(pos);
}
return sum;
}
void updateLazy(int pos,ll val){
while (pos<N){
lazy[pos]+=val;
pos+=lowbit(pos);
}
}
ll getLazy(int pos){
ll sum=0;
while(pos>0){
sum+=lazy[pos];
pos-=lowbit(pos);
}
return sum;
}
void init(){
memset(data,0, sizeof(data));
memset(lazy,0, sizeof(lazy));
}
int main(){
int n,q;
while(scanf("%d%d",&n,&q)!=EOF){
init();
ll x;
for(int i=0;i<n;i++){
scanf("%lld",&x);
update(i+1,x);
}
for(int i=0;i<q;i++){
char ch;
int l,r;
ll x;
scanf(" %c",&ch);
if(ch=='Q'){
scanf("%d%d",&l,&r);
cout<<getSum(r)<<' '<<getLazy(r)<<' '<<getSum(l-1)<<' '<<getLazy(l-1)<<endl;
printf("%lld\n",getSum(r)+getLazy(r)*r-getSum(l-1)-getLazy(l-1)*(l-1));
}else{
scanf("%d%d%lld",&l,&r,&x);
update(l,-(l-1)*x);
updateLazy(l,x);
update(r+1,r*x);
updateLazy(r+1,-x);
}
}
}
return 0;
}