树状数组进阶

树状数组是众多数据结构中常数较小,简单好写的,很多 ds 题都应该优先考虑树状数组。

接下来介绍树状数组的几种常见套路。

  • 单点加,区间求和

  • 区间加,单点查询

  • 区间加,区间求和

  • 二维树状数组,包括上面 \(3\) 个操作

  • 单点修改,求全局 \(k\) 小值

  • 求前驱,后继,排名等平衡树操作

单点加,区间求和

这是 $单点修改,区间求前缀$ 的模板
int lowbit(int x) { return x & (-x); }
void add(int x, int k)
{
	for (int i = x; i <= n; i += lowbit(i)) tr[i] += k;
}
int query(int x)
{
	int res = 0;
	for (int i = x; i; i -= lowbit(i)) res += tr[i];
	return res;
}

区间前缀和作差即可。

区间加,单点查询

考虑维护差分数组,求和时求的是差分数组,最后加上这个数原来的值即可。

区间加,区间求和

还是考虑差分数组。

记差分数组为 \(c_i\) ,原数组为 \(a_i\) ,则 \(a_i = a_{i-1} + c_i\)

\[\begin{aligned} \sum _ {i = 1} ^ n a_i &= \sum _ {i = 1} ^ n \sum _ {j = 1} ^ i c_i \\ &= \sum _ {i = 1} ^ n c_i \times (n - i + 1) \\ &= \sum _ {i = 1} ^ n c_i \times (n + 1) - \sum _ {i = 1} ^ n c_i \times i\end{aligned} \]

而我们不能够通过 \(\sum c_i\) 推出 \(\sum c_i \times i\) ,那么每次修改分别维护 \(\sum c_i\)\(\sum c_i \times i\) 即可。

二维树状数组

其实是树状数组套树状数组,每行中 \(tr_n\) 的管辖范围是 \(\sum _ {i = n - lowbit(n) + 1} ^ n a_n\) 而行与行之间还有额外的管辖范围,不多赘述。

单点修改,求全局 \(k\) 小值

这里由于是全局,所以可以使用权值树状数组,值域过大的话离散化即可。

考虑倍增枚举区间长度,当前右端点 \(x\) 加上 \(2 ^ i\) ,若小于等于 \(x\) 的数超过或者等于 \(k\) ,就不加,否则 \(x\) 加上 \(2^i\)\(sum\) 加上 \(tr_{x + 2 ^ i}\)

\(i\)\(log_2 n\) 枚举到 \(0\)\(x\)\(sum\) 初始都为 \(0\) ,最后答案就是 \(x + 1\)

求前驱,后继,排名等平衡树操作

求排名,就是求出 \((\sum _ {i = 1} ^ {x - 1} tr_i) + 1\)

求前驱,可以先把 \(x\) 的排名算出来,再用刚才求 \(k\) 小值的方法求出 \(x\) 最后一个数量不为 \(0\) 的数。

求后继,还是先求 \(x\) 的排名,再求出第一个大于 \(x\) 且数量不为 \(0\) 的数。

posted @   小队长wtz  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示