【codevs1082】线段树练习3——树状数组(改段求段)

题目链接

经典的改段求段模型,题目都告诉你要用线段树做了,当然这种操作用树状数组来写就够了。

这里需要用到两个辅组数组X和Y,每次操作时,相当于:

  X[l]+=val;X[r+1]-=val;Y[l]+=-1*val*(l-1);Y[r+1]+=r*val;

以上修改代价是O(logn)的。

对于求1~l的和,答案就是:

  get_sum(l)=X.sum(l)*l+Y.sum(l)

那么每次查询的答案就是:

  get_sum(r)-get_sum(l-1)

解释:

首先X.sum(l)与改段求点型类似,为l这个点增加的值,也就是说X.sum(l)*l表示如果把前l个数增加的值都看成和l相同的话答案是多少。

但是前l个点增加的值显然不一定和l相同啊,咋办咧?

这时候就要看式子的后半部分Y.sum(l)了:

  Y.sum(l)相当于求出前l个数增加的部分有多少是被算多的了,因为之前已经先*-1了所以直接加上就好。

查询代价O(logn),和线段树相同。

特别的,l-1可能为0,需要特判。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 #define lowbit(x) x&-x
 6 typedef long long LL;
 7 const int N=2e5+10;
 8 LL n,q,a[N],X[N],Y[N];
 9 int read(){
10     int ans=0,f=1;char c=getchar();
11     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
12     while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();}
13     return ans*f;
14 }
15 LL sum(int r,LL c[]){
16     LL ans=0;
17     if(!r)ans=c[0];
18     for(int i=r;i;i-=lowbit(i))ans+=c[i];
19     return ans;
20 }
21 void add(int r,int v,LL c[]){
22     if(!r){c[0]+=v;return;}
23     for(;r<=n;r+=lowbit(r))c[r]+=v;
24 }
25 LL get_sum(int x){return sum(x,X)*x+sum(x,Y);}
26 int main(){
27     n=read();
28     for(int i=1;i<=n;i++)a[i]=a[i-1]+read();
29     q=read();
30     while(q--){
31         int op=read(),l=read(),r=read(),v;
32         if(!(op-1)){
33             v=read();
34             add(l,v,X);add(r+1,-v,X);add(l,-1*v*(l-1),Y);add(r+1,r*v,Y);
35         }
36         else printf("%lld\n",get_sum(r)-get_sum(l-1)+a[r]-a[l-1]);
37     }
38     return 0;
39 }
codevs1082

 

posted @ 2017-10-14 07:46  Child-Single  阅读(327)  评论(0编辑  收藏  举报