📅  最后修改于: 2023-12-03 15:06:52.373000             🧑  作者: Mango
Fenwick树也叫二叉索引树(BIT),它是一种可以快速计算数列的前缀和的数据结构。相较于前缀和,Fenwick树具有更优秀的时间复杂度。
本文将介绍如何使用Fenwick树来实现在L到R范围内查询大于K的元素数。
通过使用Fenwick树,将元素数值作为数组下标,然后通过树状数组支持单点修改和前缀查询,从而快速实现对L到R范围内大于K的元素进行查询。
具体实现步骤如下:
离散化有一定的内存开销,但可以将数值较大的元素进行有效的压缩。当输入数据规模较大时,使用离散化进行优化会有显著的效果。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
// Fenwick树
struct bit {
ll c[N];
int n;
void init(int n) {
this->n = n;
memset(c, 0, sizeof(c));
}
int lowbit(int x) { return x & -x; }
void add(int x, int v) {
while (x <= n) {
c[x] += v;
x += lowbit(x);
}
}
ll query(int x) {
ll ans = 0;
while (x) {
ans += c[x];
x -= lowbit(x);
}
return ans;
}
ll query(int l, int r) {
return query(r) - query(l - 1);
}
};
int a[N], b[N], n, q;
ll ans[N];
bit t;
// 离散化
void init() {
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
}
}
// 处理问题
void solve() {
sort(a + 1, a + n + 1);
t.init(n);
int q = 0;
while (q--) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
l = lower_bound(b + 1, b + n + 1, l) - b;
r = upper_bound(b + 1, b + n + 1, r) - b - 1;
if (l > r) continue;
ans[q] = t.query(l, r) - t.query(l, k - 1);
}
}
int main() {
// 输入数组和查询
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
init();
// 离散化后处理问题
solve();
// 输出结果
for (int i = 0; i < q; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}
本算法的时间复杂度为$O(n\log n + q\log n \log x)$,其中:
Fenwick树的单次修改和查询的复杂度均为$O(\log n)$,而离散化的复杂度为$O(n\log n)$。因此该算法的总复杂度为$O(n\log n + q\log n \log x)$。
本算法的空间复杂度为$O(n)$。
本文介绍了如何使用Fenwick树在L到R范围内查询大于K的元素数量。在实际应用中,可以通过离散化优化时间复杂度,实现更高效的计算。如果读者对Fenwick树还不太熟悉,建议先阅读有关Fenwick树的基础知识。