主席树(可持久化线段树)学习笔记
主席树学习笔记
1.理解:
一种可持久化数据结构,本质上是可持久化线段树,若加上前缀和思想便为主席树。
其思想为:单点修改时,只有被修改的点到根的一条链上的点会发生改变,建一个新根,添一条新链,并与不改变的点相连。以此为基础,建立权值线段树,记录每个值(可以预先离散)出现的次数,将序列中的点看作增加某个值出现的次数。
开新点时与动态开点线段树相似。
2.功能:
求静态/动态的区间第k大。
首先,对于区间[1,r]的第k大,我们可以在权值线段树每个节点x:(代表区间[a,b])处判断节点x的左儿子y所代表区间[a,(a+b)/2]中值的出现次数与k的大小情况,如果出现次数sum[y]>=k,第k大数定在[a,(a+b)/2]区间中,否则在区间[(a+b)/2+1,b]中,最后a=b时的a即是第k大数。
当区间[1,r]推广到[l,r]时,只有出现次数由sum[r号版本]变为sum[r号版本]-sum[(l-1)号版本],其它的就一样喽。
那如何从静态推广到动态呢?
先思考暴力算法,若修改第x个数,将x号版本到n号版本的权值线段树都修改一遍。
这岂不是又是区间[x,n]的修改操作?
我们考虑用最简单的区间修改数据结构——树状数组来维护。
注:需要注意,我们需要建一棵0号版本的初始树(满的线段树),否则后继版本无法继承,且l=1时,无l-1号版本。
3.代码:
<1>.静态主席树:
1 #include<bits/stdc++.h> 2 #define re register 3 using namespace std; 4 const int N=2e5+6; 5 int n,m,rt[N],son[N<<5][2],sum[N<<5],id=0,a[N],b[N],p,t,t1,t2,t3; 6 inline int read() 7 { 8 int T=0,F=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} 10 while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); 11 return F*T; 12 } 13 void build(int l,int r,int &x) 14 { 15 if(!x) x=++id; 16 sum[x]=0; 17 if(l==r) return; 18 int mid=((l+r)>>1); 19 build(l,mid,son[x][0]),build(mid+1,r,son[x][1]); 20 } 21 void update(int l,int r,int &x,int y,int q) 22 { 23 if(!x) x=++id; 24 sum[x]=sum[y]+1; 25 if(l==r) return; 26 int mid=((l+r)>>1); 27 if(q<=mid) update(l,mid,son[x][0],son[y][0],q),son[x][1]=son[y][1]; 28 else update(mid+1,r,son[x][1],son[y][1],q),son[x][0]=son[y][0]; 29 } 30 int kth(int l,int r,int x,int y,int q) 31 { 32 while(l<=r) 33 { 34 if(l==r) return l; 35 int mid=((l+r)>>1); t=sum[son[x][0]]-sum[son[y][0]]; 36 if(q>t) q-=t,l=mid+1,x=son[x][1],y=son[y][1]; 37 else r=mid,x=son[x][0],y=son[y][0]; 38 } 39 } 40 int main() 41 { 42 n=read(),p=read(); 43 for(re int i=1;i<=n;++i) a[i]=read(),b[i]=a[i]; 44 sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1;//离散化 45 rt[0]=0; build(1,m,rt[0]);//建初始树 46 for(re int i=1;i<=n;++i) 47 { 48 t=lower_bound(b+1,b+m+1,a[i])-b;//t为离散后的编号 49 update(1,m,rt[i],rt[i-1],t);//下一个历史版本的修改 50 } 51 while(p--) 52 { 53 t1=read(),t2=read(),t3=read(); 54 printf("%d\n",b[kth(1,m,rt[t2],rt[t1-1],t3)]); 55 } 56 return 0; 57 }
<2>.动态主席树:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=100006; 4 int n,m,id=0,rt[N<<9],sum[N<<9],son[N<<9][2],a[N<<1],b[N<<1],tot1,tot2,t,num,o,e[106],f[106],num1,num2; 5 char c[12]; 6 struct xd 7 { 8 int t2,t3,t4; 9 char t1; 10 }wew[N]; 11 inline int read() 12 { 13 int T=0,F=1; char ch=getchar(); 14 while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} 15 while(ch>='0'&&ch<='9') T=(T<<1)+(T<<3)+(ch-48),ch=getchar(); 16 return F*T; 17 } 18 void update(int l,int r,int &x,int q,int p) 19 { 20 if(!x) x=++id; 21 sum[x]+=p; 22 if(l==r) return; 23 int mid=((l+r)>>1); 24 if(q<=mid) update(l,mid,son[x][0],q,p); 25 else update(mid+1,r,son[x][1],q,p); 26 } 27 int query(int l,int r,int q) 28 { 29 while(l<=r) 30 { 31 if(l==r) return l; 32 tot1=tot2=0; 33 for(int i=1;i<=num1;++i) tot1+=sum[son[e[i]][0]]; 34 for(int i=1;i<=num2;++i) tot2+=sum[son[f[i]][0]]; 35 int mid=((l+r)>>1); 36 if(q<=tot1-tot2) 37 { 38 r=mid; 39 for(int i=1;i<=num1;++i) e[i]=son[e[i]][0]; 40 for(int i=1;i<=num2;++i) f[i]=son[f[i]][0]; 41 } 42 else 43 { 44 q=q-tot1+tot2,l=mid+1; 45 for(int i=1;i<=num1;++i) e[i]=son[e[i]][1]; 46 for(int i=1;i<=num2;++i) f[i]=son[f[i]][1]; 47 } 48 } 49 } 50 inline int lowbit(int x){return x&(-x);} 51 void add(int u,int v,int w){ 52 for(int i=u;i<=n;i+=lowbit(i)) update(1,num,rt[i],v,w); 53 } 54 int getsum(int r,int l,int q) 55 { 56 num1=num2=0; 57 for(int i=r;i;i-=lowbit(i)) e[++num1]=rt[i]; 58 for(int i=l;i;i-=lowbit(i)) f[++num2]=rt[i]; 59 //e、f数组里是需要修改的节点的编号 60 return query(1,num,q); 61 } 62 int main() 63 { 64 n=read(),m=read(); o=n; 65 for(int i=1;i<=n;++i) b[i]=read(),a[i]=b[i]; 66 for(int i=1;i<=m;++i) 67 { 68 scanf("%s",c),wew[i].t1=c[0],wew[i].t2=read(),wew[i].t3=read(); 69 if(c[0]=='C') b[++o]=wew[i].t3; 70 else wew[i].t4=read(); 71 } 72 //存入输入和修改,一并离散 73 sort(b+1,b+o+1); 74 num=unique(b+1,b+o+1)-b-1; 75 for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+num+1,a[i])-b,add(i,a[i],1); 76 //权值离散 77 for(int i=1;i<=m;++i) 78 { 79 if(wew[i].t1=='Q') printf("%d\n",b[getsum(wew[i].t3,wew[i].t2-1,wew[i].t4)]); 80 else add(wew[i].t2,a[wew[i].t2],-1),a[wew[i].t2]=lower_bound(b+1,b+num+1,wew[i].t3)-b,add(wew[i].t2,a[wew[i].t2],1); 81 } 82 return 0; 83 }
<3>.可持久化线段树:
例题:洛谷P3919 [模板]可持久化数组(可持久化线段树/平衡树)
1 #include<bits/stdc++.h> 2 #define re register 3 using namespace std; 4 const int N=1e6+6; 5 int n,m,son[N<<5][2],id=0,num[N<<5],opt,t1,t2,t3,a[N],rt[N]; 6 inline int read() 7 { 8 int T=0,F=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} 10 while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); 11 return F*T; 12 } 13 void build(int l,int r,int &x) 14 { 15 if(!x) x=++id; 16 if(l==r){num[x]=a[l]; return;} 17 int mid=((l+r)>>1); 18 build(l,mid,son[x][0]),build(mid+1,r,son[x][1]); 19 } 20 void update(int l,int r,int &x,int y,int q) 21 { 22 if(!x) x=++id; 23 if(l==r){num[x]=t3; return;} 24 int mid=((l+r)>>1); 25 if(q<=mid) update(l,mid,son[x][0],son[y][0],q),son[x][1]=son[y][1]; 26 else update(mid+1,r,son[x][1],son[y][1],q),son[x][0]=son[y][0]; 27 } 28 int query(int l,int r,int x,int q) 29 { 30 while(l<=r) 31 { 32 if(l==r) return num[x]; 33 int mid=((l+r)>>1); 34 if(q<=mid) r=mid,x=son[x][0]; 35 else l=mid+1,x=son[x][1]; 36 } 37 } 38 int main() 39 { 40 n=read(),m=read(); 41 for(re int i=1;i<=n;++i) a[i]=read(); 42 build(1,n,rt[0]); 43 for(re int i=1;i<=m;++i) 44 { 45 t1=read(),opt=read(),t2=read(); 46 if(opt==1) t3=read(),update(1,n,rt[i],rt[t1],t2); 47 else rt[i]=++id,printf("%d\n",query(1,n,rt[t1],t2)),son[id][0]=son[rt[t1]][0],son[id][1]=son[rt[t1]][1]; 48 } 49 return 0; 50 }