一、可持久化线段树其实是由不同版本的线段树组成的。
二、第i棵线段树就是前i个点的权值线段树。
三、对于一个新版本的线段树只有logn个结点发生了变化,所以只需要新开logn个结点。
四、普通可持久化线段树只能解决静态问题,如果涉及修改结点值需要用到树套树。
以255. 第K小数为例,放一个模板:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//权值线段树就是对桶数组维护一个区间和
int n, q, a[100005], root[100005], cnt;
vector alls;
struct node{
int l, r;//左右儿子编号
int num;//区间内数字个数
}tr[4*100005+100005*20];
int find(int x){
return lower_bound(alls.begin(), alls.end(), x)-alls.begin()+1;//从1开始
}
int build(int l, int r){
int id = ++cnt;
if(l == r) return id;
int mid = l+r>>1;
tr[id].l = build(l, mid);
tr[id].r = build(mid+1, r);
return id;
}
//pre是基树上的结点编号
int insert(int L, int l, int r, int x){
int id = ++cnt;
tr[id] = tr[L];
tr[id].num++;
if(l == r) return id;
int mid = l+r>>1;
if(x <= mid) tr[id].l = insert(tr[L].l, l, mid, x);
else tr[id].r = insert(tr[L].r, mid+1, r, x);
return id;
}
//在R-L的树上询问
int query(int L, int R, int l, int r, int k){
if(l == r) return l;
int mid = l+r>>1, num = tr[tr[R].l].num-tr[tr[L].l].num;
if(k <= num) return query(tr[L].l, tr[R].l, l, mid, k);
else return query(tr[L].r, tr[R].r, mid+1, r, k-num);
}
signed main()
{
cin >> n >> q;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
alls.push_back(a[i]);
}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
for(int i = 1; i <= n; i++)
a[i] = find(a[i]);
root[0] = build(1, alls.size());
for(int i = 1; i <= n; i++)
root[i] = insert(root[i-1], 1, alls.size(), a[i]);
for(int i = 1; i <= q; i++){
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", alls[query(root[l-1], root[r], 1, alls.size(), k)-1]);
}
return 0;
}