之前也看了不少相关的博客,但是都没咋弄明白,十篇文章不如一张图,十张图不如一段视频,在小破站看到了一段视频,才真正搞明白视频连接
单点修改,区间查询
void add(int x,int k){//单点修改
for(;x<=N;x+=(x&-x))
t[x]+=k;
return ;
}
int ask(int x){//区间查询
int ans=0;
for(;x;x-=(x&-x))
ans+=t[x];
return ans;
}
//add(x,k)
//ask(x);
这里t[x]需要用0(n)预处理一下
sum[0]=0;
for(i=1;i<=N;i++){//求前缀和
sum[i]=sum[i-1]+a[i];
}
for(x=1;x<=N;x++){
int cnt=x&-x;//t[x]所管辖的区间长度
t[x]=sum[x]-sum[x-cnt];
}
区间修改,单点查询
正如视频中所讲,使用树状数组维护一个差分数组,这里介绍一下差分数组的概念:对于原序列a[],差分数组b[i]=a[i]-a[i-1],故有
a [ i ] = ∑ j = 1 i b [ j ] a\left[ i\right] =\sum ^{i}_{j=1}b\left[ j\right] a[i]=j=1∑ib[j]
与上面不同的是,树状数组维护的不再是序列a[],而是a[]的差分数组b[],代码和上面完全一样,只是对t[]的初始化有些不同,如下
for(int x=1;x<=N;x++){
t[x]=a[x]-a[x-(x&-x)];
}
这里不要有些无解,t[]的初始化怎么和上面的不一样呢,按照上面的思路,应该是这样求的
a[0]=0;
for(i=1;i<=N;i++){
b[i]=a[i]-a[i-1];
}
sum[0]=0;
for(i=1;i<=N;i++){
sum[i]=sum[i-1]+b[i];
}
你会发现sum[i]不就是a[i]嘛,修改查询代码如下,和视频中的不一样的是查询代码,
//区间[l,r];
//add(l,k)
//add(r+1,-k);
//ask(x)
区间修改,区间查询
如视频所讲,需要另外一个树状数组维护i*b[i],与区间修改,单点查询不同的是,增加了add2,ask2,t2[];
void add2(ll x,ll k){
for(;x<=N;x+=(x&-x))
t2[x]+=k;
}
ll ask2(ll x){
ll ans=0;
for(;x;x-=(x&-x))
ans+=t2[x];
return ans;
}
下面是t2[]的初始化
for(i=1;i<=N;i++)
b[i]=a[i]-a[i-1];
for(i=1;i<=N;i++)
b[i]*=i;
sum[0]=0;
for(i=1;i<=N;i++)
sum[i]=sum[i-1]+b[i];
for(int x=1;x<=N;x++)
t2[x]=sum[x]-sum[x-(x&-x)];
修改代码
//对于区间[l,r];
add(l,d);
add(r+1,-d);
add2(l,l*d);
add2(r+1,(r+1)*-d);
查询代码,也是和视频中不一样的(是因为在t2[]中的初始化不一样,但是代码是正确的
int ans=(r+1)*ask(r)-ask2(r)-(l*ask(l-1)-ask2(l-1));