可持久化线段树总结

一、可持久化线段树其实是由不同版本的线段树组成的。

二、第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;
}

你可能感兴趣的:(线段树,可持久化数据结构,算法学习,算法)