主席树
又名函数式线段树。(这不重要)
一个数据结构,能访问到历史版本的数据,常用于可持久化和区间K大值,是线段树的一个升级版。专门用于区间第K大值查询问题。
主席树是可持久化线段树的一种特殊实现。
原理
首先我们要将所有数字离散化。
主席树相当于是在每个位置维护了一个线段树,线段树的节点是一个区间[x, y],这里的 x 和 y 都是离散后数字的编号。
主席树节点中维护的值,是[1, i]这个区间有数字出现的次数(这个说法初看可能不能理解,举个例子就很好懂了)。当我们查询时,就利用到了前缀和的思想。
那么,来举例吧。
7
1 5 2 6 3 7 4 //离散化后的数组
首先建树,
然后逐一插入数字。将每个数字的编号插入到相应位置上,并且把所有经过的区间的值都加1。
插入1,
插入5,
插入2,
直到全部插入完成,树是这样的。
查询的原理也很好理解。
例如要查询[2, 5]中第3大的数。
我们先把第一棵线段树和第五棵6线段树拿出来。

可以发现,将相应节点的数相减,刚刚好就是[2, 5]内某个范围内的数的个数。比如[1, 4]这个节点相减是2,就说明[2, 5]内有两个数是在1~4范围内(即2和3)。
所以对于一个区间[l, r],我们可以每次算出在[l, mid]范围内的数,如果数量>=K(K就是第K大),就往左子树走,否则就往右子树走。
int query(int ll, int rr, int l, int r, int k) {
int x = st[st[rr].l].val - st[st[ll].l].val;
if (l == r) return b[l];
int mid = l + r >> 1;
if (x >= k) return query(st[ll].l, st[rr].l, l, mid, k);
return query(st[ll].r, st[rr].r, mid + 1, r, k - x);
}
直接返回第K大的数值,而不是离散化后相应的编号。
题目
P3834 【模板】可持久化线段树 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
经典可持久化权值线段树入门题——静态区间第K小。
代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// fixed << setprecision(6)
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int MOD = 998244353;
const int N = 2e5 + 5;
int n, q, m, cnt;
int a[N], b[N], rt[N];
struct node {
int l, r, val;
}st[N << 5];
void bt(int &k, int l, int r) {
k = ++cnt;
if (l == r) return ;
int mid = l + r >> 1;
bt(st[k].l, l, mid);
bt(st[k].r, mid + 1, r);
}
void change(int &k, int kk, int l, int r, int ll) {
k = ++cnt;
st[k] = st[kk];
++st[k].val;
if (l == r) return ;
int mid = l + r >> 1;
if (ll <= mid)
change(st[k].l, st[kk].l, l, mid, ll);
else
change(st[k].r, st[kk].r, mid + 1, r, ll);
}
int query(int ll, int rr, int l, int r, int k) {
int x = st[st[rr].l].val - st[st[ll].l].val;
if (l == r) return b[l];
int mid = l + r >> 1;
if (x >= k) return query(st[ll].l, st[rr].l, l, mid, k);
return query(st[ll].r, st[rr].r, mid + 1, r, k - x);
}
void solve() {
cin >> n >> q;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
int len = unique(b + 1, b + 1 + n) - b - 1;
bt(rt[0], 1, len);
for (int i = 1; i <= n; ++i) {
int t = lower_bound(b + 1, b + 1 + len, a[i]) - b;
change(rt[i], rt[i - 1], 1, len, t);
}
while (q--) {
int l, r, k;
cin >> l >> r >> k;
cout << query(rt[l - 1], rt[r], 1, len, k) << endl;
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int _ = 1;
// cin >> _;
while (_--) solve();
return 0;
}

浙公网安备 33010602011771号