树状数组(C++)
模板化的数据结构,必背。
树状数组的核心思想在于,两两相邻的相加,然后汇总到上方,再继续两两相邻相加。但是要注意的是,上层的两两相邻并不是指在数组位置中的相邻,而是因为他俩所包含的区间相邻。
举例如下:
第二层: 10 10=3+7
第一层: 3 7 3=1+2, 7=3+4
原始数据: 1 2 3 4
实际上,你确实可以把他看做一个倒着来的杨辉三角求解过程,只不过杨辉三角是为了继续拓展三角的下端,而树状数组是要把数据汇总起来。
lowbit函数:获取上一层的位置
很多人都不理解什么是lowbit函数,为什么能这样。实际上我个人认为,目前阶段(单指OI竞赛)没太多必要去真正理解lowbit背后的思想,只需要记得他会在数组中自己找一个合适的位置帮你存储对应的数据就可以了。
return x & -x;
Add函数:修改某个数字,并修改树上所有信息
通过lowbit函数,将修改的值一层一层的加上去。函数中,x为需要修改的位置(此处修改为加法),v则是增加的值。
void add(int x, int v){
for(int i=x; i<=n; i+=lowbit(i))
//i+=lowbit(i)是为了寻找下一个位置
tr[i]+=v;//修改区间的总和
}
query函数:查询对应区间
得到对应区间的总值,但是是前缀和的形式。如果要查询其中一个区间的和,则需要后区间减去前区间即可。
int query(int x){
int res = 0;
for(int i=x; i; i -= lowbit(i))
//此处的i-=lowbit(i)就相当于一层一层向下找对应区间
res += tr[i];
return res;
}
int rangeQuery(int x, int y){
return query(y) - query(x-1);
}
以上代码总结起来就可以得到以下样例代码:
class TreeArray{
private:
vector<int> tr;
int n;
public:
void init(vector<int> g){
for(int i=1; i<=g.size(); i++)
//由于树状数组需要从1位开始,所以vector<int> g要再输入前先push_back([任意数字]); 这样就能让数据从[1]位开始,而此处tr.push_back(0);是为了直接填充对应量的数据防止读取错误。
tr.push_back(0);
n = tr.size();
for(int i=1; i<n; i++)
add(i, g[i]);
}
int lowbit(int x){
return x & -x;
}
void add(int x, int v){
for(int i=x; i<=n; i += lowbit(i))
tr[i]+=v;
}
int query(int x){
int res = 0;
for(int i=x; i; i -= lowbit(i))
res += tr[i];
return res;
}
int rangeQuery(int x, int y){
return query(y) - query(x-1);
}
};
是的,你可以把他做成一个Snippet,然后直接调用一整个树状数组。会和线段树一起放出对应的snippet。