主席树 是函数式线段树,是一种持久化数据结构。对于每次修改,我们只需该次修改所改动的结点新建一遍,其余的结点跟上一历史版本一样即可。如果每次都是单点修改的话,那么就只会增加 (log n) 个新结点。
由于动态开点的原因,左右儿子就不能通过完全二叉树找左右儿子那种方式来找了,必须要记录自己的左右儿子编号。
假设当前主席树是前 (i−1) 次操作之后的状态,根为 (root[i−1]) ,一共有 (tot) 个结点,现在要进行第 (i) 次操作。
- (root[i] = ++tot;pre = root[i-1];now=root[i];)
- 如果修改点在左子树内,且 (pre e 0)(前 (i-1) 次操作后至少已经有一棵线段树),那么就令 (now) 的右儿子等于 (pre) 的右儿子。然后新建一个点作为 (now) 的左儿子,(pre=lson[pre];now=lson[now]) 。右子树同理。
- 如果到这条修改路径的底端则回溯更新结点信息,否则重复第二步。
从 (root[i]) 开始访问,就可以访问到 (1~i) 次操作之后的数据结构。
题目
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 100005;
const int M = maxn * 40;
int n, q, m, tot;
int a[maxn], t[maxn], T[maxn];
int lson[M], rson[M], sum[M];
void init()
{
for(int i = 1; i <= n; i++) t[i] = a[i];
sort(t + 1, t + n + 1);
m = unique(t + 1, t + n + 1) - (t + 1);
}
int _hash(int x)
{
return lower_bound(t + 1, t + m + 1, x) - t;
}
int build(int l, int r)
{
int root = tot++;
sum[root] = 0;
if(l != r){
int mid = (l + r) >> 1;
lson[root] = build(l, mid);
rson[root] = build(mid + 1, r);
}
return root;
}
int update(int root, int pos, int val)
{
int newroot = tot++, ret = newroot;
sum[newroot] = sum[root] + val;
int l = 1, r = m;
while(l < r){
int mid = (l + r) >> 1;
if(pos <= mid){
lson[newroot] = tot++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
}
else{
rson[newroot] = tot++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid + 1;
}
sum[newroot] = sum[root] + val;
}
return ret;
}
int query(int left, int right, int k)
{
int l = 1, r = m;
while(l < r){
int mid = (l + r) >> 1;
if(sum[lson[left]] - sum[lson[right]] >= k){
r = mid;
left = lson[left];
right = lson[right];
}
else{
l = mid + 1;
k -= (sum[lson[left]] - sum[lson[right]]);
left = rson[left];
right = rson[right];
}
}
return l;
}
int main()
{
while(~scanf("%d%d", &n, &q)){
tot = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
init();
T[n + 1] = build(1, m);
for(int i = n; i >= 1; i--){
int pos = _hash(a[i]);
T[i] = update(T[i + 1], pos, 1);
}
while(q--){
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d
", t[query(T[l], T[r + 1], k)]);
}
}
return 0;
}