1.单点更新,区间求和
void update(int p, int x) //给位置p增加x 单点更新
{
while(p <= n)
{
sum[p] += x;
p += p & -p;
}
}
int sum(int p) //求位置p的前缀和 区间查询 (前缀和)
{
int res = 0;
while(p)
{
res += sum[p];
p -= p & -p;
}
return res;
}
int range_sum(int l, int r)
{ //区间求和
return sum(r) - sum(l - 1);
}
2.单点查询,区间更新
void update(int p, int x) //sum表示差分数组,x传入的是差值 ,p是数组下标
{
while(p <= n)
{
sum[p] += x;
p += p & -p;
}
}
void range_update(int l, int r, int x) //给区间[l, r]加上x
{
update(l, x);
update(r + 1, -x);
}
int sum(int p) //单点查询(差分数组的前缀和)
{
int res = 0;
while(p) res += sum[p], p -= p & -p;
return res;
}
详解1.
代码中有lowbit函数
int lowbit(x) x是数组下标
{
return x & -x;
}
lowbit函数就是求某一个数的二进制表示中最低的一位1
,举个例子,x = 6
,它的二进制为110
,那么lowbit(x)
就返回2
,因为最后一位1
表示2
。-x就是把x这个数的二进制取反+1,如-10的二进制就是-1010=0101+1=0110,然后用1010&0110,答案就是0010了
在将更新查询之前先放一张经典的图:
查询区间的和:
首先建立一个sum数组。sum数组表示代表后缀和,,sum[i]
表示从第i
个元素向前数lowbit(i)
个元素。
那么求前6项的和就是求:sum(6)+sum(4),因为sum(6)表示从第6个元素向前数2个元素的和,也就是a[5]+a[6].
可以得出结论,求第i项的前缀和就是求sum [ i ] + sum [ i - lowbit( i ) ]+.......如果下标大于0,就一直加。
单点更新:
首先看图我们可以得到:
- 后缀和的长度是2的幂;
- 上一层后缀和的长度是下一层后缀和长度的两倍;
- 下一层后缀和只要补上自己后缀和的长度就可以得到上面层的后缀和(图中的虚框框),注意,是上面的后缀和,而不是上一层的后缀和,这个性质就是更新操作的依据;
- 最后一位
1
右边有多少个0
(可以用log2(lowbit(x))log2(lowbit(x))表示)就表示这一层有多少个直系子层(子层的意思就是这一层的和包含下面某一层的和)。
更新一个点会影响到他的父结点,如果这个点+1,那么父节点全部加+。要通过这个点找到父节点。根据性质3。要找到6的父节点就是sum【6+lowbit(6(110)】=sum(8)
2.详解
运用差分的思想,此时sum数组代表差值。传入的x是差值