树状数组
树状数组的基本用途是:维护序列的前缀和。
基本思想是:对于给定的序列a,建立一个数组c,其中c[x]保存序列a在区间[x-lowbit(x)+1, x]中所有数的和。
int lowbit(int x)//返回lowbit的位数
{
return x & (-x);
}
可以想象一个树形结构(如果N不是2的整数次幂,则为森林结构)。
- 每个内部节点c[x]保存以它为根节点的子树中所有叶子节点的和。
- 每个内部节点c[x]的子节点的个数等于lowbit(x)的位数。
- 除了根节点外,每个内部节点c[x]的父节点是c[x+lowbit(x)]。
- 树的深度是O(logN)。
这个树结构支持两种操作:
- 查询前缀和:O(logN)
求出[1, x]的值。如果需要[l, r]的值,可以使用ask(r )-ask(l -1 )查询。
int ask(int x)
{
int ans = 0;
for (; x; x -= lowbit(x))
ans += c[x];
return ans;
}
- 单点增加值:O(logN)
给a[x]加上数值y。
可以用add操作来进行初始化——新建全0的数组c,给每个位置x执行操作add(x, a[x])。这样初始化得时间复杂度为O(NlogN)。
void add(int x, int y)
{
for (; x <= N; x += lowbit(x))
c[x] += y;
}