昨天对树状数组的理解好像有点问题,今天又重新理解了一下,首先是这三个最基本的代码
int lowbit(int x){
return x&(-x);
}
void add(int x,int k){
for(;x<=n;x+=lowbit(x))
t[x]+=k;
}
int ask(int x){
int ans=0;
for(;x;x-=lowbit(x))
ans+=t[x];
return ans;
}
场景1:单点修改,单点/区间查询。
单点x修改add( x , k ),单点x查询ask( x ) - ask( x-1),区间 [ l , r ] 查询ask( r ) - ask( l-1),
原数组就是 t [ x ] ,不难看出单点查询就是区间查询的子情况, t [ x ]表示子节点值之和,所以add 改变时也要算上父节点
例题
场景2:区间修改,单点查询。
区间 [ l , r ] 修改add( l , k ),add( r+1,-k ),单点x查询ask( x )
这个地方与上一个不同,原数组是 a [ x ], 差分数组 t [ x ]= a [ x ] - a [ x-1 ] + t [x ]子节点之和,输入a [ x ]后计算完 t [ x ]就不再对a [ x ]进行任何操作
这时候的的ask( x )也发生了改变,t 数组相加求的是修改后的 a [ x ]值,对于区间 [ l , r ] 改变 t [ l ] 和 t [ r+1 ],但不能仅改变 t [ l ] 和 t [ r+1 ], t [ l ] 改变时,也改变了他的父节点,单个改变和也改变,实际上只有 a [ l ] - a [ l-1 ] 和a [ r+1 ] - a [ r ] 改变了,a [ l+1 ] - a [ l ]到a [ r ] - a [ r-1 ] 是不变的
例题
场景3:区间修改,区间查询。
这个我认为比较特殊。先附上例题(虽然标签是线段树,但却是典型的区间修改+区间查询),再附上代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
long long t1[100010],t2[100010],a[100010];
int n,m;
int lowbit(int x){
return x&(-x);
}
void add(int x,long long k){
for(int i=x;i<=n;i+=lowbit(i))
{
t1[i]+=k;
t2[i]+=x*k;
}
}
long long ask(int x){
long long ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=(x+1)*t1[i]-t2[i];
return ans;
}
int main()
{
cin>>n>>m;
a[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
add(i,a[i]-a[i-1]);
}
while(m--)
{
int s;
scanf("%d",&s);
if(s==1)
{
int x,y;
long long k;
scanf("%d %d %lld",&x,&y,&k);
add(x,k);
add(y+1,-k);
}
else
{
int x,y;
scanf("%d %d",&x,&y);
printf("%lld\n",ask(y)-ask(x-1));
}
}
return 0;
}
首先是和场景2一样的差分数组,该题设为 t 1
a[1]+a[2]+……+a[r-1]+a[r]
=t1[1]+(t1[1]+t1[2])+……+(t1[1]+……+t1[r])
=(t1[1]*(r))+(t1[2]*(r-1))+……(t1[r]*1)
=r*(t1[1]+t1[2]+……+t1[r])-(t1[1]*0+t1[2]*1+……+t1[r]*(r-1))
=(r+1)*(t1[1]+t1[2]+……+t1[r])-(t1[1]*1+t1[2]*2+……+t1[r]*r)
对于 (t1[1]*1+t1[2]*2+……+t1[r]*r) *这一部分,用 t1 的求和比较难写,所以设另一个数组 t 2 ,使得 t 2 [ i ] = t 1 [ i ] * i,并且保证和 t 1 同时更新,这里我的更新 add 是把两个同时在一起写的(因为懒),一个要注意的点是:代码中 add 的 x 是恒定不变的,所以不能像 / 场景2那样不设 i 直接直接对 x 进行改变/,因此我用 i 来作为循环的数,就在这个地方我犯了一个低级的错误把 i >0;(或直接 i; 也可以)写成了 i >=0找了好久才找到,写下来提醒一下自己
回顾一下这两天,感觉第一天只搞懂了原理,第二天才开始看应用做了基础的几个题,确实效率有点低。