📅  最后修改于: 2023-12-03 14:49:57.016000             🧑  作者: Mango
细分树(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范围内的元素可以拆分成以下两个问题:
对于第一个问题,我们可以使用细分树来实现区间查询的效果。可以使用线段树或者树状数组来实现。
对于第二个问题,我们可以将查询到的元素按照值从小到大排序,然后在排好序的元素序列中使用二分法查找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;
}