【可持久化数据结构】主席树(可持久化线段树)

在学习数据结构的路上渐行渐远。。。
学习了主席树(可持久化线段树)/(函数式线段树)。
简单的介绍一下可持久化数据结构的意思,大概就是可以保存历史版本的数据结构。
对于线段树而言,其的可持久化就是对每次操作建立不同版本的线段树,但显然,这样的时空复杂度过大,是不可接受的。
考虑只有单点修改的情况,一次操作影响的部分不过只有一条链,故可以只对这部分建立新版本,这大概就是可持久化线段树的基本思想。
对于查询,只需要使用不同版本的根进行查询即可,与线段树没有特别大的差别。
每次修改与查询的时间复杂度均为\(O(n \log_{2} n)\)
显然,每次修改只会影响 \(O(log_{2}n)\)个线段树上的点,故每次修改只会新建立\(O(log_{2} n )\)个点,空间复杂度为\(O(n \log_{2} n)\)
接下来就是如何新建点的问题,只需要动态开点,其中一个孩子仍为旧版本的点,新修改的链上点则为新建点即可。
例题为POJ 2104 / POJ 2761 / luogu 3834
模板如下:

#include <stdio.h>
#include <algorithm>
#define mid (l+r>>1)
#define MN 100005
#define MM (((1<<17)*17)<<1)
#define R register
inline int read(){
	R int x; R char c; R bool f;
	for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
	for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
	return f?-x:x;
}
int ls[MM],rs[MM],v[MM],cnt,rt[MN],val[MN],rk[MN],vrk[MN],n,m;
inline bool cmp(int a,int b){return val[a]<val[b];}
inline int copy(int old){ls[++cnt]=ls[old],rs[cnt]=rs[old],v[cnt]=v[old];return cnt;}
void modify(int val,int &x,int l,int r){
	x=copy(x);++v[x];if (l==r) return;
	if (val<=mid) modify(val,ls[x],l,mid);
	else modify(val,rs[x],mid+1,r);
}
int query(int rk,int lq,int rq,int l,int r){
	if (l==r) return l;R int tmp=v[ls[rq]]-v[ls[lq]];
	if (rk<=tmp) return query(rk,ls[lq],ls[rq],l,mid);
	else return query(rk-tmp,rs[lq],rs[rq],mid+1,r);
}
int main(){
	n=read(),m=read();
	for (R int i=1; i<=n; ++i)
		val[i]=read(),rk[i]=i;
	std::sort(rk+1,rk+n+1,cmp);
	for (R int i=1; i<=n; ++i) vrk[rk[i]]=i;
	for (R int i=1; i<=n; ++i)
		rt[i]=rt[i-1],modify(vrk[i],rt[i],1,n);
	for (R int i=1; i<=m; ++i){
		R int l=read(),r=read(),k=read();
		printf("%d\n",val[rk[query(k,rt[l-1],rt[r],1,n)]]);
	}
	return 0;
}
posted @ 2017-08-23 12:48  Melacau  阅读(321)  评论(0编辑  收藏  举报