树状数组区间修改区间查询
其实在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们!