📌  相关文章
📜  使用细分树查询给定索引范围内的值在A到B范围内的元素(1)

📅  最后修改于: 2023-12-03 14:49:57.016000             🧑  作者: Mango

使用细分树查询给定索引范围内的值在A到B范围内的元素

细分树(subdivision tree)是一种多叉树,用于解决查询连续子区间问题。细分树可以应用于各种范围查询问题,例如区间最大子段和、区间第k大等。

本文介绍如何使用细分树查询给定索引范围内的值在A到B范围内的元素。

细分树的构建

细分树的构建过程是递归的。对于一个区间[l, r],我们可以将其分成两个区间[l, m]和[m+1, r],并在细分树上构建出对应的节点。对于每个叶子节点,它代表的区间就是[l, r]。对于每个非叶子节点,它代表的区间就是其子节点代表的区间的并集。

例如,对于区间[1, 8],我们可以将其分成[1, 4]和[5, 8]两个子区间,然后在细分树上构建出两个节点。如下图所示:

        1-8
        / \
      1-4 5-8

我们可以递归地将[1, 4]和[5, 8]继续划分,直到只剩一个元素为止。对于长度为n的区间,细分树的构建过程的时间复杂度是O(nlogn)。

细分树的查询

查询给定索引范围内的值在A到B范围内的元素可以拆分成以下两个问题:

  1. 查询给定索引范围内的所有元素
  2. 对于查询出的所有元素,找到A到B范围内的元素

对于第一个问题,我们可以使用细分树来实现区间查询的效果。可以使用线段树或者树状数组来实现。

对于第二个问题,我们可以将查询到的元素按照值从小到大排序,然后在排好序的元素序列中使用二分法查找A和B的位置,找到范围为[A, B]的元素。

示例代码

这里给出一个使用C++实现的细分树查询给定索引范围内的值在A到B范围内的元素的示例代码。

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 100000;

int n, m, a[MAXN], root[MAXN];
struct Node {
    int l, r, sum;
} t[MAXN * 40];
int cnt;

void build(int& now, int l, int r) {
    now = ++cnt;
    if (l == r) {
        t[now].sum = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(t[now].l, l, mid);
    build(t[now].r, mid + 1, r);
    t[now].sum = t[t[now].l].sum + t[t[now].r].sum;
}

void update(int& now, int pre, int l, int r, int pos, int delta) {
    now = ++cnt;
    t[now] = t[pre];
    if (l == r) {
        t[now].sum += delta;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) update(t[now].l, t[pre].l, l, mid, pos, delta);
    else update(t[now].r, t[pre].r, mid + 1, r, pos, delta);
    t[now].sum = t[t[now].l].sum + t[t[now].r].sum;
}

int query(int now, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return t[now].sum;
    int mid = (l + r) >> 1, ans = 0;
    if (ql <= mid) ans += query(t[now].l, l, mid, ql, qr);
    if (qr > mid) ans += query(t[now].r, mid + 1, r, ql, qr);
    return ans;
}

int kth(int now, int l, int r, int k) {
    if (l == r) return l;
    int mid = (l + r) >> 1;
    if (t[now].sum <= k) return -1;
    if (t[t[now].l].sum >= k) return kth(t[now].l, l, mid, k);
    else return kth(t[now].r, mid + 1, r, k - t[t[now].l].sum);
}

void solve() {
    sort(a + 1, a + n + 1);
    build(root[0], 1, n);
    for (int i = 1; i <= n; i++) {
        int x = lower_bound(a + 1, a + n + 1, a[i] - m) - a - 1;
        int y = upper_bound(a + 1, a + n + 1, a[i] - 1) - a - 1;
        if (!x) x++;
        if (y >= i) continue;
        root[i] = root[i - 1];
        if (x != i) update(root[i], root[i - 1], 1, n, i, 1);
        if (y != 0) update(root[i], root[i - 1], 1, n, kth(root[i], 1, n, x), -1);
    }
    int q;
    cin >> q;
    while (q--) {
        int l, r, A, B;
        cin >> l >> r >> A >> B;
        int x = upper_bound(a + 1, a + n + 1, B) - a - 1;
        int y = lower_bound(a + 1, a + n + 1, A) - a;
        if (x < y) {
            cout << 0 << endl;
            continue;
        }
        int ans = query(root[r], 1, n, y, x);
        if (l != 1) ans -= query(root[l - 1], 1, n, y, x);
        cout << ans << endl;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    solve();
    return 0;
}
参考资料