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

ZJYelizaveta

2017-10-14 22:03:34

Solution

### 描述 用树状数组来维护大小为序列 $n$,要求支持区间修改,单点查询。 $n \leq 500000, m \leq 500000$ ### 分析 树状数组的本职是支持单点修改,区间查询的。这样把操作反过来以后应该怎样才能继续用树状数组来维护呢? 这里应用 `差分` 的思想。 比如原来树状数组的每一个叶子结点都是 $a[i]$,那么现在存放的就是 $c[i] = a[i] - a[i - 1]$。 $a[i] = c[i] + c[i - 1] + c[i - 2] + \cdots + c[2] +c[1]$,因此若要单点查询,那么直接查询前缀和即可。 至于区间修改很容易理解,就是 $modify[l, +k], modify[r + 1, -k]$,至于为什么自己在树状数组上画一下就可以了。 另,这里也提一下在要支持区间修改,区间查询的时候怎么办? 一样用树状数组来维护一个差分数组,区间修改同上。 $$\because a[i] = \sum_{j = 1}^{i}c[j] $$ $$\therefore \sum_{i = 1}^{n}a[i] = \sum_{i = 1}^{n}\sum_{j = 1}^{i}c[j] = \sum_{i = 1}^{n}(n - i + 1)c[i] = (n + 1)\sum_{i = 1}^{n}c[i] - \sum_{i = 1}^{n}c[i] \times i$$ 开两个树状数组来维护这两项即可。 时间复杂度 $\Theta(nlogn)$ ### 代码 ```C++ #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; template<typename T> T readIn() { T x(0), f(1); char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();} return x * f; } const int MAX_N = 500000 + 3; const int MAX_M = 500000 + 3; const int INF = 0x3f3f3f3f; int n, m; int a[MAX_N]; namespace fenwickTree { int vec[MAX_N]; inline void init() { memset(vec, 0, sizeof vec); } inline int lowbit(int x) { return x & (-x); } inline void modify(int id, int x) { while (id <= n) { vec[id] += x; id += lowbit(id); } } inline int query(int id) { int res = 0; while (id >= 1) { res += vec[id]; id -= lowbit(id); } return res; } } using namespace fenwickTree; int main() { n = readIn<int>(), m = readIn<int>(); for (int i = 1; i <= n; ++i) a[i] = readIn<int>(); init(); int last = 0; for (int i = 1; i <= n; ++i) { modify(i, a[i] - last); last = a[i]; } while (m--) { int opt = readIn<int>(); if (opt == 1) { int l = readIn<int>(), r = readIn<int>(), k = readIn<int>(); modify(l, k); modify(r + 1, -k); } else { int pos = readIn<int>(); int ans = query(pos); printf("%d\n", ans); } } return 0; } ```