主席树/可持久化线段树总结
2017-07-15 16:19
155 查看
【介绍】
主席树也就是函数式线段树,运用了可持久化思想从而可以在短时间寻找到一段区间的K大值,是一个非常优秀的算法,但是属于离线算法。
【思想】
对于给定的一个序列,每次询问某一段自区间的K大值。对于这类题型,就是主席树登场的时候,首先对这个序列离散,假设去重后有x个元素,那么用线段树来维护区间中数的个数。对于询问的某一段区间[L,R],利用容斥思想,用R的前缀减去L的前缀便是答案。所以要分别对序列0~k(0<=k<=x)的元素构造大小相同的线段树。然后类似于平衡树的思想求K大值。但是构造n棵线段树的空间是O(2∗x2)(Ps:这是对于指针记录左右儿子的线段树,一般线段树大家都知道是O(4∗x2)),对于x达到10的6,7次的题目完全不适用。
进一步可以发现,其实构造好[0,k](0<=k<x)的线段树后,可以对于构造[0,k+1]的线段树只需要修改一些节点的数值,并非全部,所以并不需要重新构造一颗线段树,只需要在原先线段树的基础上插入一些修改后的节点共用一些子节点就行了,如下图:
![](http://img.blog.csdn.net/20170715161810003?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ0hOV0pE/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这样可以实现可持久化,降低了空间乃至时间复杂度。
对于数组的大小也就是log2(N)*N(N表示元素个数,因为线段树的深度最多也就是log2(N))。
【核心代码】
主席树也就是函数式线段树,运用了可持久化思想从而可以在短时间寻找到一段区间的K大值,是一个非常优秀的算法,但是属于离线算法。
【思想】
对于给定的一个序列,每次询问某一段自区间的K大值。对于这类题型,就是主席树登场的时候,首先对这个序列离散,假设去重后有x个元素,那么用线段树来维护区间中数的个数。对于询问的某一段区间[L,R],利用容斥思想,用R的前缀减去L的前缀便是答案。所以要分别对序列0~k(0<=k<=x)的元素构造大小相同的线段树。然后类似于平衡树的思想求K大值。但是构造n棵线段树的空间是O(2∗x2)(Ps:这是对于指针记录左右儿子的线段树,一般线段树大家都知道是O(4∗x2)),对于x达到10的6,7次的题目完全不适用。
进一步可以发现,其实构造好[0,k](0<=k<x)的线段树后,可以对于构造[0,k+1]的线段树只需要修改一些节点的数值,并非全部,所以并不需要重新构造一颗线段树,只需要在原先线段树的基础上插入一些修改后的节点共用一些子节点就行了,如下图:
这样可以实现可持久化,降低了空间乃至时间复杂度。
对于数组的大小也就是log2(N)*N(N表示元素个数,因为线段树的深度最多也就是log2(N))。
【核心代码】
struct PT { PT* son[2]; int l,r,s; }tem[maxm],*Null=tem,*len=Null,*ro[maxn]; void Pushup(PT* k) {k->s=k->son[0]->s+k->son[1]->s;} PT* New(int L,int R,int p)//构造新节点 { ++len; len->l=L; len->r=R; len->s=p; len->son[0]=len->son[1]=Null; return len; } PT* Build(int L,int R)//建树 { PT* now=New(L,R,0); if (L==R) return now; int mid=(R-L>>1)+L; now->son[0]=Build(L,mid); now->son[1]=Build(mid+1,R); Pushup(now); return now; } PT* Insert(PT* k,int where)//插入 { int L=k->l,R=k->r; PT* now=New(L,R,k->s); now->son[0]=k->son[0]; now->son[1]=k->son[1]; if (L==R) {now->s++; return now;} int mid=(R-L>>1)+L; if (where<=mid) now->son[0]=Insert(now->son[0],where); else now->son[1]=Insert(now->son[1],where); Pushup(now); return now; } int Query(PT* L,PT* R,int k)//询问K大值,类似平衡树思想 { if (L->l==L->r) return b[L->l]; int p=R->son[0]->s-L->son[0]->s;//容斥思想 if (p>=k) return Query(L->son[0],R->son[0],k); else return Query(L->son[1],R->son[1],k-p); }
相关文章推荐
- 可持久化线段树(主席树)【舰娘系列】【自编题】
- 可持久化线段树/主席树 基础原理和例题
- 【HDU4348】To The Moon-主席树(可持久化线段树)区间修改+区间询问
- 可持久化线段树(主席树)【舰娘系列】【自编题】
- [ZOJ2112][可持久化线段树(主席树)][树状数组]Dynamic Rankings[好题]
- 主席树(可持久化线段树)入门专题
- hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
- [Data]可持久化线段树-主席树
- P3834 【模板】可持久化线段树 1(主席树)
- 主席树/函数式线段树/可持久化线段树
- 主席树(可持久化线段树)学习笔记
- 【模板】可持久化线段树(主席树)
- luoguP3834 【模板】可持久化线段树 1(主席树)
- 主席树——可持久化权值线段树
- 洛谷.3834.[模板]可持久化线段树(主席树 静态区间第k小)
- 主席树(可持久化线段树)
- 主席树——可持久化值域线段树
- 主席树(可持久化线段树)学习笔记
- 【模板】主席树/函数式线段树/可持久化线段树