树状数组能实现的功能线段树都能实现,所以之前是没打算仔细看它的。但今天做了一道题,,让我感受到了树状数组相对于线段树所具有的的优势。
看下面三张图。
第一张图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, 此时会陷入死循环。。