权值树状数组
权值树状数组
权值数组研究的是数组的 值域 问题,经常应用于不重视数组的 元素顺序,更注重 元素内容 的场景。
单点修改,查询全局第 kkk 小
现在需要对一个数组进行两种操作
pos x
表示将 a[pos]a[pos]a[pos] 的权值改成 xxx。k
查询数组的全局第 kkk 小元素是谁。
如果 aaa 数组的 值域 以及每次 修改范围 不大,就可以用 cnt[i]cnt[i]cnt[i] 表示权值为 iii 的数的 出现次数。
题目要求的是全局第 kkk 小元素,可以使用 二分 cntcntcnt 前缀和 的方式来完成。
所以需要动态维护 cntcntcnt 数组的前缀和,此时就可以用树状数组来维护。
初始时,创建一个 大小为值域 的数组 tree[i]tree[i]tree[i],维护以 [i−lowbit(i)+1,i][i-lowbit(i)+1,i][i−lowbit(i)+1,i] 的区间信息。
然后进行 nnn 次 单点修改 将 aaa 的信息更新完,每次单点修改就是令 tree[a[pos]]tree[a[pos]]tree[a[pos]] 及其所有祖先加 111。
对于操作 111,等价为 tree[a[pos]]tree[a[pos]]tree[a[pos]] 及其所有祖先减 111, tree[x]tree[x]tree[x] 及其所有祖先加 111。
对于操作 222,二分前缀和,但是每次求前缀和是 O(logn)O(logn)O(logn),故总时间是 O(log2n)O(log^2n)O(log2n)。
二分前缀和是求出一个正整数 xxx 满足 xxx 的前缀和小于 kkk 且 x+1x+1x+1 的前缀和大于等于 kkk 。
此时 x+1x+1x+1 就是全局第 kkk 小。
其实可以利用 树状数组的特性 来实现操作 222。
已知 tree[i]tree[i]tree[i] 维护的是以 iii 结尾长度为 lowbit(i)lowbit(i)lowbit(i) 的区间信息,当 iii 是 222 的幂的时候,维护的就是前缀和信息。
此时这个前缀和信息可以 O(1)O(1)O(1) 获得。
设初始状态 x=0x=0x=0,sum=0sum=0sum=0,其中 xxx 表示第 kkk 小的数应该大于的数,sumsumsum 表示当前前缀和的值。
于是可以从最高的二进制位 log(n)log(n)log(n) 开始检查,对于当前二进制位 jjj 而言:
- sum+tree[x+2j]≥ksum+tree[x+2^j]\ge ksum+tree[x+2j]≥k,说明我们要找的数比 x+2jx+2^jx+2j 小。
- sum+tree[x+2j]<ksum+tree[x+2^j]<ksum+tree[x+2j]<k,说明我们要找的数比 x+2jx+2^jx+2j 大,所以 x:=2j,sum:=tree[x+2j]x:=2^j,sum:=tree[x+2^j]x:=2j,sum:=tree[x+2j]。
最终找到的 x+1x+1x+1 就是全局第 kkk 小的元素。
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define INF 1e18template<typename T>// 单点修改和区间查询的树状数组
struct Fenwick {vector <T> tree;int n;Fenwick(int _n) : n(_n), tree(_n + 2){}// 求 1~x 的前缀和T getsum(int x) {T ans = 0;int L = x;while (x) {ans += tree[x];x = x - lowbit(x);}return ans;}// 区间查询T get_range_sum(int l, int r) {return getsum(r) - getsum(l - 1);}// 单点修改,是为了 range_add 服务void add(int x, T k) {while (x <= n) {tree[x] += k;x += lowbit(x);}}static inline int lowbit(int x) {return x & (-x);}T getKth(int k) {T x = 0, sum = 0;for (int i = log2(n); i >= 0; i--) {x += (1ll << i);if (x >= n || sum + tree[x] >= k) {x -= (1ll << i);} else {sum += tree[x];} }return x + 1;}
};
void slove () {Fenwick<int> tree(n);
}signed main () {ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);slove();
}