树状数组知识点整理二(待)
树状数组知识点整理二(待)
lowbit()的证明
由于证明仅需了解基本规律,所以仅简述补码的含义,原码,反码即它们的优缺点不做赘述。
补码即将数各位按位取反后加一的结果,补码在计算机中用来储存负数。
以c++中32bit的int类型为例 \(4_{10}=100_2;-4_{10}=11111111111111111111111111111100_2\) ,注意-4的二进制表示最高位是符号位,4的二进制表示由于1之前都为0而省略,并不是不存在。
在int类型的取反中三十二位都要参与运算。
将 \(100_2\) 取反 \(11111111111111111111111111111011_2\) 加一 \(11111111111111111111111111111100_2\) 。
实际取反在操作中就代表加负号。
这是输出int原值和取反的代码。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
using namespace std;
char s[100100];
int main() {
int x;
cin >> x;
itoa(x, s, 2);
printf("%s\n", s);
itoa(-x, s, 2);
printf("%s", s);
return 0;
}
了解了补码的运算规律,就可以开始尝试证明 \(lowbit(x) = x\&-x\) 的正确性了。
当 \(x\) 的二进制表示下末尾为 \(1\) :
显然取反后末尾为 \(0\) ,加一后为 \(1\) 。
而前面由于取反不可能有两位同时为 \(1\) 。
此时, \(x\&-x=1\) 。
当 \(x\) 的二进制表示下末尾有一串 \(0\) :
取反后,末尾一串 \(0\) 前面的 \(1\) 变为了 \(0\) 。
加一后又变为了 \(1\) 。
末尾一串 \(0\) 由于加一进位还是 \(0\) 。
所以显然此时 \(x\&-x=lowbit(x)\) 。
这里都是在不超过c++中int极限下论证的,比如 \(-2^{32}\) 的绝对值超出了int的界限,再论证就没有意义了。
区间加单点查
在整理一中,我尝试了以浅薄的思考说明原始树状数组对单点修改区间查询的适配性,其实树状数组也可以通过一定的变换支持别的操作。
称之为原始的树状数组,是因为我认为其基于最常见的数组意义构建,如果变换维护的数组意义,便能得到其他的操作。
但是无论如何,我认为,它在所维护的数组上更适配单点修改区间查询。
在其他地方,或许你听到过将区间修改转变为单点修改的一种方法,差分。
构建基于原始数组的差分数组,我们就能将修改原始数组的区间转变为修改差分数组的两个点,将查询原始数组的一个点,转变为查询差分数组的一个区间。
记差分数组为 \(C\) 。
若对于原数组我们想要将 \(A_x\) 到 \(A_y\) 中的值都加上一个常数 \(m\) ,那么在差分数组中,我们就可以在 \(C_x\) 上加 \(m\) ,而在 \(C_{y+1}\) 上减 \(m\) 。

浙公网安备 33010602011771号