树状数组讲解

树状数组能实现的功能线段树都能实现,所以之前是没打算仔细看它的。但今天做了一道题,,让我感受到了树状数组相对于线段树所具有的的优势。
看下面三张图。
在这里插入图片描述在这里插入图片描述在这里插入图片描述第一张图unaccepted的都是用线段树写的出现了tle,第二张图是用线段树写的优化后ac的详细信息,第三张是用树状数组写的。仔细看看后两张图的第九第十组样例,,不难发现线段树太慢了(相对而言)。于是,,我还是研究研究树状数组吧。。

在这里插入图片描述上图是树状数组的结构,其核心是lowbit()函数。

int lowbit(int x)
{
	return x & (-x);
}

lowbit返回值是x的二进制里第一个1所在位置的对应值。关于lowbit函数有五个特点:

1.lowbit的值与节点所在的层数是对应的,值越大,层数越高。
2.lowbit(x)的值为x这个节点过管辖区间范围的大小。
3.节点x加上lowbit(x)的值为x的父亲节点。
4.节点x减去lowbit(x)的值为x所管辖范围最左边的前一位,即若管辖区间为【l, r】,其值为l - 1。
5.节点x分别减去1, 2, 4, 8…即2的k次方为其子节点。

树状数组的操作基本上都是围绕着五点展开的。这些能掌握,树状数组的就能掌握了。

最后,附上部分代码

单点修改, 区间查询:

int lowbit(int x)
{
	return x & (-x);
}

void update(int i, int val)
{
	for( ; i <= n; i += lowbit(i))
		t[i] += val;
}

int query(int i)
{
	int ans = 0;
	for(; i > 0; i -= lowbit(i))
		ans += t[i];
	return ans;
}

区间最值,区间查询:

void update(int i)
{
	for(; i<= n; i += lowbit(i))
	{
		t[i] = a[i];
		for(int j = 1; j < lowbit(i); j <<= 1)
		{
			t[i] = max(t[i], t[i - j]);
		}
	}
}

int query(int l, int r)
{
	int ans = 0;
	while(r >= l)
	{
		ans = max(ans, a[r--]);
		for(; r - lowbit(r)  >= l; r -= lowbit(r))//不能写为r - lowbit(r) + 1 >= l
		{
			ans = max(ans, t[r]);
		}
	}
	return ans;
}

查询里面不能写为 r - lowbit(r) + 1 >= l 的原因是若查询【1,5】时,在查询4时 r - lowbit(4) = 0,而 r - lowbit(4) + 1 = 1 >= 1, 此时会陷入死循环。。

发布了73 篇原创文章 · 获赞 15 · 访问量 8112

猜你喜欢

转载自blog.csdn.net/ln2037/article/details/102024499
今日推荐