题解:P3368 【模板】树状数组 2

· · 题解

P3368 题解:树状数组2

题目传送门

题目描述

给定一个长度为 n 的数组 f ,以及 m 次操作。操作分为两种:

  1. 区间修改:将数组 f 中下标在 [x, y] 范围内的元素都加上 z
  2. 单点查询:查询数组 f 中下标为 x 的元素的值。

解题思路

这道题可以使用树状数组来高效地处理区间修改和单点查询。树状数组是一种支持单点修改和区间查询的数据结构,但通过差分的思想,我们可以将其扩展为支持区间修改和单点查询。

代码解析

树状数组的定义:

$lowbit(x)$ :返回 $ x $ 的二进制表示中最低位的 $ 1 $ 所对应的值。 $add(x, k)$ :在树状数组的第 $ x $ 个位置加上 $ k $ ,并更新其父节点。 $search(x)$ :查询树状数组中前 $ x $ 个元素的和。 ### 初始化: 读取数组 $ f $ 的初始值。 ### 处理操作: 区间修改:对于操作 $ 1 $ $ x $ $ y $ $ z $ ,我们通过差分的思想,在 $ x $ 位置加上 $ z $,在 $ y+1 $ 位置减去 $ z $ 。这样,查询时就可以通过前缀和得到每个位置的实际值。 单点查询:对于操作 $ 2 $ $ x $,我们查询树状数组中前 $ x $ 个元素的和,并加上 $ f[x] $ 的初始值,得到最终的结果。 ## 代码 ```cpp #include <bits/stdc++.h> //万能头 using namespace std; const int N = 500005; int n, m; int f[N], fa[N]; // 返回 x 的二进制表示中最低位的 1 所对应的值 int lowbit(int x) { return x & -x; } // 在树状数组的第 x 个位置加上 k void add(int x, int k) { while (x <= n) { fa[x] += k; x += lowbit(x); } } // 查询树状数组中前 x 个元素的和 int search(int x) { int ans = 0; while (x != 0) { ans += fa[x]; x -= lowbit(x); } return ans; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> f[i]; for (int i = 1, op, x, y, z; i <= m; i++) { cin >> op; if (op == 1) { // 区间修改 cin >> x >> y >> z; add(x, z); add(y + 1, -z); } else { // 单点查询 cin >> x; cout << f[x] + search(x) << "\n"; } } } ``` ## 复杂度分析: 时间复杂度: 每次区间修改和单点查询的时间复杂度均为 $ O(log n) $ 。 总的时间复杂度为 $ O(m log n) $。 空间复杂度:树状数组 $ fa $ 的空间复杂度为 $ O(n) $ 。 ## 总结: 通过使用树状数组和差分的思想,我们可以高效地处理区间修改和单点查询操作。这种方法在时间复杂度和空间复杂度上都表现良好,适用于处理大规模数据的场景。