树状数组区间修改区间查询

树状数组区间修改区间查询


其实在csdn老博客上写过了。然而那个实在太丑,不想搬过来。(其实是为了秀一波LaTeX公式)

测试题

首先,设(C_i)([i,n])区间的共同增量。

修改:

差分思想,修改(l)(r+1).

查询:

原数组没多大用,前缀和搞定不管。

假定树状数组开始全为0

约定:(sum[i,j])表示([i,j])区间的和

强行推公式:

(sum[i,i]=sum_{j=1}^{i}C_i)

上面显然。不懂的就可以走了。。。

普通树状数组查询用到了差分思想,即(sum[l,r]=sum[1,r]-sum[1,l-1]).下面就来试着求(sum[1,r]).

(sum[1,r])

(=sum_{i=1}^{r}sum[i][i])

(=sum_{i=1}^{r}sum_{j=1}^{i}C_i)

(=sum_{i=1}^{r}C_i×(r-i+1))

(=sum_{i=1}^{r}C_i×(r+1)-sum_{i=1}^{r}C_i×i)

所以发现:只需要新维护一个树状数组存储(C_i×i)就行了。

这个在修改里一并实现。


Code

// It is made by XZZ
#include<cstdio>
#include<algorithm>
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il ll gi(){
    rg ll x=0;rg char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
#define maxn 1000010
#define lb(o) (o&-o)
int n,m;
ll S[maxn],C[maxn],Ci[maxn];
il vd add(ll*ar,int pos,ll num){while(pos<=n)ar[pos]+=num,pos+=lb(pos);}
il ll sum(ll*ar,int pos){ll ret=0;while(pos)ret+=ar[pos],pos-=lb(pos);return ret;}
il vd Updata(int l,int r,ll num){
    add(C,l,num);if(r^n)add(C,r+1,-num);
    add(Ci,l,num*l);if(r^n)add(Ci,r+1,-num*(r+1));
}
il ll Query(int r){return(r+1)*sum(C,r)-sum(Ci,r)+S[r];}
int main(){
    n=gi(),m=gi();
    rep(i,1,n)S[i]=gi()+S[i-1];
    int x,y;
    while(m--)
    if(gi()==1)x=gi(),y=gi(),Updata(x,y,gi());
    else x=gi(),y=gi(),printf("%lld
",Query(y)-Query(x-1));
    return 0;
}

大牛分站开氧气,80ms,暂时第3版
很快要被刷下去了
%烂打zkw的dalao们!

原文地址:https://www.cnblogs.com/xzz_233/p/7554755.html