主席树/可持久化线段树简介(洛谷P3834/P3919)
2017-12-24 10:30
393 查看
前置技能
线段树废话主席树
介绍
我太懒了所以直接引用一下主席树又称函数式线段树,顾名思义,也就是通过函数来实现的线段树,至于为什么叫主席树,那是因为是fotile主席创建出来的这个数据结构
算法应用及实现
主席树最经典的应用就是在线求区间第k大。运用前缀和的思想,我们把序列的每一个前缀都建一颗线段树。每一个节点存的是这个节点对应值出现的次数,所以查询[l,r]的时候只需要把r这棵树“减去”l−1这棵树就行了。而我们只需知道他们的大小关系,因此预处理的时候先把它离散化一下。
但是如果把序列的每一个前缀都“真的”建树的话当然会MLE,而我们发现每次新加进去一个数最多只需要改变log2个节点的权值,其他的都不变。所以我们可以通过共用上一棵树的节点来减小空间开销。
差不多长这样(出处见右下角):
可以发现本来要新建7个点,通过共用之后只新建了3个点。这样一来,总空间就变成了n(1+log2n)了(1是因为要建一颗空树)。
查询的时候像平衡树一样,如果k小于右子树大小查询右子树第k大,否则查询左子树的第k-sum大。
代码(洛谷P3834):
#include<cctype> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 using namespace std; struct tree{ //主席树的左右儿子编号并不是x*2和x*2+1 //sum存子树大小(即这个子树的总次数) int ls,rs,sum; }t[N*20]; int n,m,num,nd,rt[N*20],a ,b ; inline char readc(){ static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++; } inline int _read(){ int x=0,f=1; char ch=readc(); while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); } while (isdigit(ch)) x=x*10+ch-48,ch=readc(); return x*f; } void ntlz(int &x,int l,int r){//建空树 t[x=++nd].sum=0; if (l==r) return; int mid=l+r>>1; ntlz(t[x].ls,l,mid),ntlz(t[x].rs,mid+1,r); } void build(int &x,int l,int r,int fa,int p){//建树 t[x=++nd].ls=t[fa].ls,t[x].rs=t[fa].rs; t[x].sum=t[fa].sum+1; if (l==r) return; int mid=l+r>>1; if (p<=mid) build(t[x].ls,l,mid,t[fa].ls,p); else build(t[x].rs,mid+1,r,t[fa].rs,p); } int srch(int p,int q,int l,int r,int k){//查询 if (l==r) return l; int mid=l+r>>1,df=t[t[q].ls].sum-t[t[p].ls].sum;//直接相减 if (k<=df) return srch(t[p].ls,t[q].ls,l,mid,k); else return srch(t[p].rs,t[q].rs,mid+1,r,k-df); } int main(){ n=_read(),m=_read(); for (int i=1;i<=n;i++) a[i]=b[i]=_read(); sort(b+1,b+n+1),num=unique(b+1,b+n+1)-(b+1);//离散 nd=0,ntlz(rt[0],1,num); for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+num+1,a[i])-b;//a就是离散后的数组 for (int i=1;i<=n;i++) build(rt[i],1,num,rt[i-1],a[i]);//建树 while (m--){ int l=_read(),r=_read(),k=_read(); printf("%d\n",b[srch(rt[l-1],rt[r],1,num,k)]); } return 0; }
可持久化线段树
其实就是主席树介绍
看名称就知道是什么东西了。。。算法应用与实现
看名称就知道是干嘛的了。。。支持查询/修改某一历史版本的信息。
同主席树一样,对于每一次修改,不用重新建树,而是改变路径上的节点信息,共用其他节点。查询的话直接查就行了。
然后就差不多了。。。
以洛谷P3919为例
#include<cctype> #include<cstdio> #include<cstring> #include<algorithm> #define N 1000005 using namespace std; struct tree{ int ls,rs,x; }t[N*40]; int n,m,nd,num,rt ,a ; inline char readc(){ static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++; } inline int _read(){ int x=0,f=1; char ch=readc(); while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); } while (isdigit(ch)) x=x*10+ch-48,ch=readc(); return x*f; } void build(int &x,int l,int r){//建树 int mid=l+r>>1; x=++nd; if (l==r) { t[x].x=a[l]; return; }; build(t[x].ls,l,mid),build(t[x].rs,mid+1,r); } void nsrt(int &x,int l,int r,int p,int w,int fa){//修改 t[x=++nd].ls=t[fa].ls,t[x].rs=t[fa].rs;//共用节点 if (l==r) { t[x].x=w; return; } int mid=l+r>>1; if (p<=mid) nsrt(t[x].ls,l,mid,p,w,t[fa].ls); else nsrt(t[x].rs,mid+1,r,p,w,t[fa].rs); } int srch(int &x,int l,int r,int p,int fa){//查询 t[x=++nd].ls=t[fa].ls,t[x].rs=t[fa].rs;//这道题要求两个操作都新增一个版本 if (l==r) return t[x].x=t[fa].x; int mid=l+r>>1; if (p<=mid) return srch(t[x].ls,l,mid,p,t[fa].ls); else return srch(t[x].rs,mid+1,r,p,t[fa].rs); } int main(){ n=_read(),m=_read(); for (int i=1;i<=n;i++) a[i]=_read(); build(rt[0],1,n); while (m--){ int v=_read(),f=_read(),p=_read(),w; if (f==1) nsrt(rt[++num],1,n,p,w=_read(),rt[v]); else printf("%d\n",srch(rt[++num],1,n,p,rt[v])); } return 0; }
相关文章推荐
- POJ2104-K-th Number-区间第k大-可持久化线段树/主席树
- POJ 2104 kth number 主席树(可持久化线段树)[指针实现]
- [BZOJ2588]Count on a tree(可持久化权值线段树|主席树)
- HDU 2665(主席树/可持久化线段树)
- 主席树/可持久化线段树总结
- 可持久化线段树 简介
- HDU 2665 Kth number [可持久化线段树 主席树]
- 【可持久化数据结构】主席树(可持久化线段树)
- HDU 4417 Super Mario [可持久化线段树 主席树]
- [Data]可持久化线段树-主席树
- 主席树(可持久化线段树) 静态第k大
- 【模板】可持久化线段树(主席树)
- spoj3267 D-query 主席树(可持久化线段树)
- 可持久化线段树——主席树
- hdu2665 求区间第k大(小?)【主席树or可持久化线段树or函数式线段树】
- 主席树——可持久化权值线段树
- 主席树练习1——P3834 【模板】可持久化线段树 1(主席树)
- 主席树(可持久化线段树)学习笔记
- 主席树(可持久化线段树)
- poj-2104 K-th Number[主席树/函数式线段树/可持久化线段树]