可持续化线段树

可持续化线段树,也叫主席树,很久以前【也没多久】写了一篇求区间第k大的,那时候理解不深,现在再写一个加深理解

可持续化,具体来说就是可以访问历史版本,以实现跨时间的操作。

怎么实现呢?
试想我们修改线段树的时候,修改前是旧版本,修改后是新版本,想想新版本和旧版本有什么区别?
区别就在于从根节点到被修改点的路径上的点改变了,其他的点是不变的

那么这个时候我们新建一个根节点,对于不变的儿子指向旧版本的儿子,对于有变化的儿子就新建一个节点
这样一来我们有每一个时刻的根节点,就可以访问到每个时刻的线段树
空间会不会暴呢?
每条路径只有logn个点,m次修改我们只多了mlogn个点,可以承受
但实在空间卡得很紧时就要用指针了

比如洛谷P3931

下面给出指针版题解代码【并非完全是普遍的写法,由题简化】:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 1000005,maxm = 10000005,INF = 1000000000;

inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
	return out * flag;
}

int N,M,A[maxn];

struct node{
	int v;
	node *ls,*rs;
	node() {ls = rs = NULL;}
};
node* rt[maxn];
int rti = 0,pos;

void Build(node **u,int l,int r){
	*u = new node;
	if (l == r){
		(*u)->v = A[l];
	}else {
		int mid = l + r >> 1;
		Build(&(*u)->ls,l,mid);
		Build(&(*u)->rs,mid + 1,r);
	}
}

void change(node *pre,node **u,int l,int r,int v){
	(*u) = new node;
	*(*u) = *pre;
	if (l == r){
		(*u)->v = v;
	}else {
		int mid = l + r >> 1;
		if (mid >= pos) change(pre->ls,&(*u)->ls,l,mid,v);
		else change(pre->rs,&(*u)->rs,mid + 1,r,v);
	}
}

int Query(node *u,int l,int r){
	if (l == r) return u->v;
	else {
		int mid = l + r >> 1;
		if (mid >= pos) return Query(u->ls,l,mid);
		else return Query(u->rs,mid + 1,r);
	}
}

int main()
{
	N = read();
	M = read();
	REP(i,N) A[i] = read();
	Build(&rt[0],1,N);
	int pre,cmd,v;
	while (M--){
		pre = read();
		cmd = read();
		pos = read();
		if (cmd & 1){
			v = read();
			change(rt[pre],&rt[++rti],1,N,v);
		}else {
			printf("%d\n",Query(rt[++rti] = rt[pre],1,N));
		}
	}
	return 0;
}

posted @ 2017-10-28 21:08  Mychael  阅读(271)  评论(0编辑  收藏  举报