树状数组

一、树状数组是什么

树状数组就是把普通数组改成的长得像树的数组

它长什么样大家可以自行搜索【放不上图片让我很懵】【欢迎私信告诉我怎么放图片】

其中有一部分数是原数组几个数的和

比方说2,就是1和2两个数的和;4,就是1、2、4三个数的和

可以看出这些数都是2的幂

这里要引入一个概念:lowbit

lowbit是指一个数的二进制表示的最后一个1所表示的数值

比方说6,6的二进制为000110

它最后一位1表示的大小为2,即lowbit(6)=2

这个lowbit其实就是树状数组每一个元素所代表的原数组的元素的数量

即树状数组中第6个元素是原数组中2个元素的和

这样的数组,就是树状数组

二、操作

1、单点修改

树状数组的一个元素是原数组中很多元素的和,所以原数组改了一个点,那么树状数组中和这个点有关的所有点都要改

因为a[i+lowbid(i)]这个元素是包含a[i]的【这句话没看懂的可以去看图】

所以如果要改a[i]也要一块把a[i+lowbid(i)]也改了

代码如下

void jia(int x,int k)
{
	while(x<=n)
	{
		t[x]+=k;
		x+=lowbit(x);
	}
}

2、询问区间和

例如,询问 l 到 r 之间的所有元素的和

通过了解前缀和我们知道s[l~r]=s[l]-s[r-1]

那么同理,询问区间和时我们只需要知道数组中的s[1~l]和s[1~r-1]

又可知f[i]代表的是i及i之前lowbit(i)个元素的和

那么就把沿途的都加上就好了

代码如下

int sum(int x)
{
	int s=0;
	while(x!=0)
	{
		s+=t[x];
		x-=lowbit(x);
	}
	return s;
}

3、区间修改

区间修改和下面的单点查询需要用到差值【不然一个一个改会TLE不要问我怎么知道的】

差值就是用一个数组【假设为f】存储a中每个元素的差

即f[i]=a[i]-a[i-1],然后我们再用一个树状数组存储f,就能很快的操作

读入代码如下

for(int i=1;i<=n;i++)
{
	scanf("%d",&a[i]);
	xiugai(i,a[i]-a[i-1]);
}

 对了读入要用scanf和printf,不然洛谷上的模板题会TLE三个点

了解过差值的人应该知道,a[i]=a[i-1]+f[i]

那么如果要修改a[l]~a[r]这个区间,只需要把f[l]+k,f[r+1]-k

这样r之后的元素就会被+k-k抵消掉成原值

树状数组的区间修改就是用了这个原理

代码如下

void xiugai(int x,int k)
{
	while(x<=n)
	{
		f[x]+=k;
		x+=lowbit(x);
	}
}
int main()
{
    scanf("%d %d %d",&y,&z,&b);
	xiugai(y,b);
	xiugai(z+1,-b);
}

4、单点查询

单点查询就可以利用差值

可以利用前面所说的sum函数也就是求区间和函数

因为a[0]=0,那么易证sum[i]的值就是a[i]的值

输出sum[i]即可

代码如下

int findsum(int x)
{
	int s=0;
	while(x)
	{
		s+=f[x];
		x-=lowbit(x);
	}
	return s;
}
int main()
{
    scanf("%d",&y);
	printf("%d\n",findsum(y));
}

5、总代码

单点修改和区间查询:https://blog.csdn.net/jkrj02/article/details/81115322

区间修改和单点查询:https://blog.csdn.net/jkrj02/article/details/81115267

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值