树状数组的实现和应用

之前也看了不少相关的博客,但是都没咋弄明白,十篇文章不如一张图,十张图不如一段视频,在小破站看到了一段视频,才真正搞明白视频连接
单点修改,区间查询
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=1ib[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));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值