主席树的学习
Posted zenyz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树的学习相关的知识,希望对你有一定的参考价值。
前言
主席树可真是个好东西
之前一直都觉得挺难的
今天一看
woc这么简单!
怎么可能,我还是太蒟蒻了
感谢akakw1大佬的指导!
正文:
一.前置知识及算法思路
1.可持久化
因为主席树是可持久化线段树,所以还是有必要了解一下可持久化
可持久化的数据结构是可以支持访问任一历史版本的(也就是每一次修改操作之前的情况)
2.如何实现
以可持久化线段树为例:
很自然的,我们可以想到对于每一个版本开一个线段树
但考虑到这样做的空间复杂度是(O(k*n*4)) (k为修改次数)
果断放弃
3.优化
考虑进行优化.
盗一张图
假如我们现在要修改的点是4(也就是区间[4,4])
可以发现区间[1,3]和区间[5,6]是完全没有改变的,
因此我们考虑利用上一版本
具体解释:当我们递归[1,6]的左子树[1,3]时发现4根本没有在其中,这也就意味着当前版本的[1,3]节点与上一版本完全一样!
因此我们直接让新开的根节点(也就是橙色的[1.6])的左子节点指向原来的根节点(也就是蓝色的[1.6])的左子节点(蓝色的[1,3]);
接着继续递归右子树[4,6],发现[6,6]也可以直接利用上一版本,以此类推
然后就发现每一次就只要开(log_2 n)个节点了!
二.例题
题目就是让你查询区间[l,r]的第k小值
离散化一下值域
这题开个可持久化值域线段树就可以啦
代码
1 #include<bits/stdc++.h> 2 #define R register int 3 #define gc getchar 4 using namespace std; 5 const int MAX_N=2e5+10,MAX_MLOGN=4e6+10; 6 int tot,root[MAX_N],n,m,q,a[MAX_N],b[MAX_N]; 7 int rd() 8 { 9 int ans=0,flag=1; 10 char ch=gc(); 11 while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘)ch=gc(); 12 if(ch==‘-‘)flag=-1,ch=gc(); 13 while(ch>=‘0‘&&ch<=‘9‘)ans=ans*10+ch-48,ch=gc(); 14 return ans*flag; 15 } 16 struct Segment_Tree{ 17 int l,r; 18 int dat; 19 }t[MAX_MLOGN]; 20 21 //l,r为左右子节点编号 22 //dat是当前版本的序列下 x的个数(L_i<=x<=R_i(i为节点编号,这里的L,R*要与结构体中的l,r加以区分)) 23 //*这里的L,R是指当前节点在线段树中所代表的值域[L,R] 24 25 int Build(int l,int r)//l,r都为值域,函数的返回值是节点编号 26 //调用入口为Build(1,m) *(1,m)为离散后的值域 27 { 28 int p=++tot; 29 t[p].dat=0; 30 //因为主席树不再是完全二叉树,不满足t[p*2]是t[p]的子节点 31 //所以直接用tot来记录节点编号 32 if(l>=r) 33 { 34 return p; 35 } 36 int mid=(l+r)>>1; 37 t[p].l=Build(l,mid); 38 t[p].r=Build(mid+1,r); 39 return p; 40 } 41 int Insert(int pre,int l,int r,int x)//pre是上一版本的同一位置的节点的编号;l,r为值域;x为值 42 //函数返回值仍然是节点编号 43 { 44 int p=++tot; 45 t[p].l=t[pre].l,t[p].r=t[pre].r; 46 t[p].dat=t[pre].dat+1;//可以先直接继承上一版本的数据 47 int mid=(l+r)>>1; 48 if(l<r) 49 { 50 if(x<=mid)//按值域划分,不用多说 51 { 52 t[p].l=Insert(t[pre].l,l,mid,x);//这里是递归t[pre]而不是t[p] 53 } 54 else 55 { 56 t[p].r=Insert(t[pre].r,mid+1,r,x);//同理 57 } 58 } 59 return p; 60 } 61 int Query(int u,int v,int l,int r,int k) 62 { 63 if(l>=r)return l; 64 int mid=(l+r)>>1; 65 int tmp=t[t[v].l].dat-t[t[u].l].dat; 66 if(tmp<k)return Query(t[u].r,t[v].r,mid+1,r,k-tmp); 67 else return Query(t[u].l,t[v].l,l,mid,k); 68 } 69 int main() 70 { 71 n=rd(),q=rd(); 72 for(R i=1;i<=n;i++) 73 { 74 b[i]=a[i]=rd(); 75 } 76 /**/ 77 sort(b+1, b+1+n); 78 m=unique(b+1,b+1+n)-b-1;//离散化 79 /**/ 80 root[0]=Build(1,m); 81 for(R i=1;i<=n;i++) 82 { 83 R t=lower_bound(b+1,b+1+m,a[i])-b; 84 root[i]=Insert(root[i-1],1,m,t); 85 } 86 for(R i=1;i<=q;i++) 87 { 88 int x=rd(),y=rd(),k=rd(); 89 R tmp=Query(root[x-1],root[y],1,m,k); 90 printf("%d ",b[tmp]); 91 } 92 return 0; 93 }
我太蒟了所以加了一大波注释(怕自己以后看不懂)
我太蒟了所以以上文字有什么问题可以在评论区回复
话说会有人看吗QAQ
以上是关于主席树的学习的主要内容,如果未能解决你的问题,请参考以下文章