一个很优美的东西。主要用于在动态数组中求区间和。

关于 lowbit()

lowbit(x)\texttt{lowbit}(x) 指的是 xx 的二进制表达式中最右边 11 所对应的值。即 xx 与上 (x)(-x) 的值。

证明:

xx 的值 (x)2(x)_2 的值 x-x 的值(补码) & 后的值
11
22 1010
33 1111 11
44 100100
55 101101 11
66 110110 1010

可以发现,xx 为奇数时,lowbit(x)=1\texttt{lowbit}(x)=1,否则,lowbit(x)=\texttt{lowbit}(x)= 最后一个 11 后面一堆 00。因为偶数二进制补码最后一位一定是 11,就会有进位。

树状数组

根据 lowbit() 建树。若要查询一个节点 xx 的关系节点:

  • 父节点:

    • xx 为左儿子,就将 x+lowbit(x)x+\texttt{lowbit}(x)
    • 若为右儿子,则 xlowbit(x)x-\texttt{lowbit}(x)
  • 儿子节点:

    • 左儿子:xx2x-\dfrac{x}{2}
    • 右儿子:x+x2x+\dfrac{x}{2}

代码

若要修改某个值,则需要将其关联的区间全部修改。

需要从下往上爬,不断找右上方的节点(不一定按照边找父亲)。途径的每个节点都需要加上给出的值。最终一直加到 nn 以后才结束。

void modify(int x,int d)//修改
{
	while(x <= n)
	{
		c[x] += d;
		x += lowbit(x);
	}
	return;
}

若要查询和,则不停找左上角的值。但要保证现在的节点编号大于 00

int sum(int x)//求和
{
	int dig = 0;
	while(x >= 1)
	{
		dig += c[x];
		x -= lowbit(x);
	}
	return dig;
}

综合:

int lowbit(int x)//函数
{
	return x & (-x);
}
void modify(int x,int d)
{
	while(x <= maxn)
	{
		c[x] += d;
		x += lowbit(x);
	}
	return;
}
int sum(int x)
{
	int dig = 0;
	while(x >= 1)
	{
		dig += c[x];
		x -= lowbit(x);
	}
	return dig;
}